8.What's the difference between extends and super in Java generics?
The bounds are defined for a type in Java generics using wildcards and extends/super. Again, the situation here is different as to whether you have a read from or write to collection.
? extends T (upper bound): This constrains the type to T or any class that is a subclass of T. It's generally used when you want to read from a collection without modifying it. This will ensure that you can work with a wider range of types, but you can't add any elements to the collection, as you don't know the exact type.
Example:
public void printList(List<? extends Number> list){
for (Number num : list) {
System.out.println(num); /* Can read elements, but can't add new elements */
}
}
In this example,? extends Number allows the method to accept lists of any type that extends Number (like Integer, Double, or Float).
? super T (lower bound): This restricts the type to T or any super-class of T. It is primarily used when you want to add elements to a collection but do not want to read from it. In this approach, you can safely add objects of type T or its subclasses but cannot guarantee the type when retrieving elements from the collection.
Example:
public void addNumber(List<? super Integer> list) {
list.add(10); /* Can safely add Integer or its subclass */
}
Here, ? super Integer allows the method to accept lists that can hold Integer or any of its superclasses, such as Number or Object.
9.What is type inference in Java generics?
Type inference in Java generics is the feature of the compiler to automatically decide the type parameter of a generic class, method or interface based on its usage context. For instance, when you declare an instance of a generic class you might not need to specify the type parameter and the compiler will decide the type depending on the declaration of the constructor or method invocation:
Box<Integer> box = new Box<>(10); /* Compiler infers type as Integer */
The compiler has inferred that type of Box as Integer because a value of the type Integer (10) was passed to its constructor. The feature of Type Inference saves boilerplate codes and makes it more clean, readable. Particularly when the context offers easy deduction the use is especially handy for type inference:
10.What is generic method in Java?
A generic method is a method declared within its own type parameter, independently of the class type parameter. This method can work on objects of many types, thereby making it reusable and type safe.
The following is the syntax for defining a generic method.
public <T> void printArray(T[] array) {
for (T element : array){
System.out.println(element);
}
}
In the code snippet, a method printArray is defined as taking an array of any type. The method's type is declared at a method level such that it need not be an independent of its class type. It can easily get the compiler to infer this for you using a call with an argument. For example: printArray(new Integer[]{1, 2, 3}).
Generic methods are a wonderful way to make code more flexible and reusable. Methods can be made to operate on a wide range of types while maintaining type safety.
11.What is the declaration of Java generic method?
A generic method in Java is that which can work with different types of objects so that code becomes more versatile and reusable as well. It is declared by indicating a type parameter in angle brackets <> before the return type of the method. In this case, the type parameter is replaced with an actual type at the moment of method invocation.
Example:
public <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
In this example:
<T> is the type parameter.
printArray can handle arrays of any type, such as integers, strings or custom objects.
This approach eliminates the need to write separate methods for each data type, streamlining code maintenance and improving efficiency.
12.How Do You Invoke a Generic Method with a Nongeneric Type Parameter?
There are two ways to invoke a generic method:
Implicit Type Inference: The type is inferred from the parameters passed to the method.
Explicit Type Specification: You specify the type explicitly while calling the method.
Example:
Integer[] intArray = {1, 2, 3, 4};
String[] strArray = {"A", "B", "C"};
/* Implicit Type Inference */
printArray(intArray); /* T is inferred as Integer */
printArray(strArray); /* T is inferred as String */
/* Explicit Type Specification */
Utility.<Integer>printArray(new Integer[]{10, 20, 30});
Using these techniques guarantees that the type performs well on many types, while maintaining the benefits of type safety.
13.Can a Generic Method be a member of a non-Generic Class?
Yes, a nongeneric method can be included in a non-generic class. Here, the class is not generic but some specific methods contained within the class can be generic. This is useful where only some of the methods should work on different types.
for example.
public class Utility {
public <T> void print(T value) {
System.out.println(value);
}
}
The Utility class is non-generic.
The print method is generic and can take any type.
Usage:
Utility utility = new Utility();
utility.print("Hello, World!"); /* T is inferred as String */
utility.print(100); /* T is inferred as Integer */
This form is flexible in the sense it provides type safe operations on a method level functionality, but it does not make the entire class generic.
14.How does type inference work for generic methods?
Type inference in Java is helpful in letting the compiler deduce the type parameter of a generic method without having an explicit declaration. This implies the compiler examines the arguments used while calling the method to infer which type should be used.
Example:
public <T> void display(T value) {
System.out.println(value);
}
display(10); /* Here, the compiler infers T as Integer */
display("Hello"); /* Here, the compiler infers T as String */
In this example:
When 10 is passed as an argument, the compiler infers that T should be an Integer.
When "Hello" is passed, it deduces that T should be a String.
This process reduces code verbosity, as you do not have to explicitly declare the type for every method call. Type inference ensures cleaner and more readable code while maintaining the flexibility of generics.
15.What Are the Advantages of Using Generic Methods?
Generic methods in Java provide many benefits that make them a very useful tool for developers. They enhance the usability, safety, and flexibility of code.
Below are the primary advantages:
1. Increased Code Reusability
This allows you to write one method that you can use on various data types. Therefore, you do not need repeat overloaded methods individually tailored to types.
2. Reduced Type Safety
With generic methods, the compiler checks the types more strictly. This helps avert innocently using incorrect types at runtime and largely minimizes runtime exceptions such as ClassCastException.
3. More Readable and Maintainable Code
By using generics, the method signature clearly shows that the method can handle any number of types. This increases code readability and, therefore, ease of understanding and maintenance.
4. Flexibility
Generic methods can work with any type: primitive wrappers, such as Integer or Double, strings and even custom classes. Such flexibility makes the code more flexible in a variety of applications.
Example:
public <T> void process(T data)
{
System.out.println("Processing: " + data);
}
process(42); // Handles Integer
process("Java"); // Handles String
process(new Date()); // Handles custom object
Generic methods can be used by developers to produce flexible, bug-free and efficient code that is also more readable and expandable when project needs evolve.
16.What are some common use cases for wildcards in Java generics?
Wildcard is very commonly used in Java generics when you want to create flexible, reusable and type safe methods or classes.
Some common use cases are:
•Reading from collections: If you just need to read from a collection and don't need to add elements, you can use an upper bounded wildcard (? extends T). This way you know you are working with a certain type or any of its subclasses and it also won't let you add invalid types.
public void printNumbers(List<? extends Number> list) {
for (Number num : list)
System.out.println(num);
}
•Writing to collections: When you wish to add elements to a collection, you may use a lower bounded wildcard (? super T). In this manner you are assured of being able to add instances of the specified type or any of its subclasses, without knowing what the actual type of the collection is.
public void addInteger(List<? super Integer> list)
{
list.add(10); /* Safe because Integer is a subtype of Number */
}
•Wildcard-based generic methods: Wildcards can be used for defining generic methods that can operate with different types of collections. Rather than creating one method for every type, you can have a single method using a wildcard and it will operate on several types.
public void printList(List<T> list) {
for (T element : list){
System.out.println(element);
}
}
•Unbounded wildcard: If you don't need to restrict the type at all, the unbounded wildcard is useful for methods that can accept any type.
public void printAnyType(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
Using wildcards, you can develop methods and classes that are very flexible and able to deal with different types in a clean and type-safe manner.
17.In what way does the use of wildcards strengthen generic type safety?
The usage of wildcards enhances generic type safety in ensuring that the types used in a collection are compatible with the type expected. Use of wildcards compels Java to enforce the bounds at compile time, thus avoiding otherwise potential invalid type assignments and also lessening runtime errors.
For example:
•Upper-bounded wildcards (? extends T): When you use an upper-bounded wildcard, you are only allowed to read elements of the collection as instances of T or its subclasses. The compiler ensures that you can't add objects of incompatible types to the collection.
public void printNumbers(List<? extends Number> list)
{
/* Only safe to read from the list */
}
• Lower-bounded wildcards (? extends T): Even when you have a lower bounded wildcard, if you have an instance of that wildcard as a parameter type, Java can prevent you from putting anything in that collection, giving you added type safety if you are adding to a collection.
public void addInteger(List<? super Integer> list)
{
list.add(10); /* Safe since list can take Integer or any supertype */
}
In both cases, wildcards avoid type mismatches and potential ClassCastException errors so that your code remains type safe.
18.Are wildcards useful for return types?
Yes, wildcards are also useful with return types, they are typically more useful with parameters. When you use wildcards in the return type, they ensure that the method is able to return a type that meets the constraints specified by the wildcard. You can use a wildcard to say that a method returns a value of a type that extends some class, as follows:
public <T> T getFirstElement(List<? extends T> list)
{
return list.get(0); /* Returns an element of a type that extends T */
}
It here takes as an input, a List of elements which are subclassed T, returning as a result the type of one element and which can also be one of the subclasses. Again, here using wild cards means the return types is sure to match elements contained in a list while retaining a form of type safety.
19.May Wildcards Appear in the Generic Class Declarations?
Generally speaking, wildcards are rarely employed in a generic class declaration but are much more frequently used when declaring methods of the class for achieving flexibility in operations involving several types. It means that a particular type parameter could be specified by the class definition, while methods use wildcards to establish broader behavior about compatibility with some other type.
For instance:
public class Box{
private T value;
public Box(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public <U> void displayBoxValue(Box extends U> box) {
System.out.println(box.getValue());
}
}
}
Here:
The class Box is defined with a generic type parameter T.
The method displayBoxValue uses the wildcard ? extends U, allowing it to accept any Box holding a type that is a subclass of U.
This approach offers flexibility for specific methods without requiring the entire class to support wildcards directly.
20.Can a stream be reused once it is consumed?
Incorrect use of wildcards in Java generics can cause compile-time errors. The compiler enforces very strict type safety rules and any operation which does not obey these will fail during compilation. Some of the most common misuses of wildcards are listed below.
1. Adding Elements to Collections with Upper Bounded Wildcards
An error is made when you are using an upper bounded wildcard (? extends T) because you can't add elements to the collection since the compiler can not guarantee type safety.
List<? extends Number> numbers = new ArrayList<>();
numbers.add(10); // Compile-time error
Here, the list is bounded to hold subtypes of Number, but the actual type is unknown. Adding an element could potentially violate type compatibility.
2. Incompatible Types with Lower-Bounded Wildcards
When you use a lower-bounded wildcard (? super T), you can add elements of type T or its subtypes, but adding unrelated types throws errors.
List<? super Integer> list = new ArrayList<>();
list.add(10); // Works fine
list.add("String"); // Compile-time error
In this example, the list can accept Integer or any of its supertypes, but trying to add something incompatible, such as String, results in a compile-time error.
Wildcard Errors Avoidance
Know Bounds: Make sure you know if the wildcard is upper (? extends T) or lower (? super T) bounded and honor the constraint.
Read-Only vs. Write Operations: Use upper-bounded wildcards for read-only operations and lower-bounded wildcards for write operations.
Compile-Time Safety: Always rely on the compiler's warnings and errors to identify potential issues with wildcard usage.
By following these guidelines, you can effectively use wildcards to write flexible and type-safe code while avoiding common pitfalls.
21.What are wildcard in java Generics and How Do They Work?
Java Generics wildcards are tokens that represent an unknown type in your code, yet increase flexibility without trading off on type safety. They are helpful when you want a class or a method to work with any number of types without listing them all out explicitly. therefore, java introduces three kinds of wildcard types:
Unbounded Wildcard (?):
It is a wildcard which can be assigned any type. This is very useful when we do not need to care about the type. For instance, List<?> will hold any kind of element but you cannot add new elements in it except null because there is no specific type defined.
Upper-Bounded Wildcard (? extends T):
This wildcard allows this type to be T or any of its subclasses.
For example, List extends Number> could refer to a list of Number or any subclass like Integer or Double. You can read from the list but adding new elements is restricted since the specific type is unknown.
Lower-Bounded Wildcard: (? super T):
It is T or any of its supertypes. So, List super Integer> can hold an Integer or any of its supertypes such as Number or Object. You can add elements of type T and its subclasses but explicit casting will be required at the time of reading elements.
Java generics wildcards find the right balance between flexibility and type safety by allowing more compatibility in the generic context, while ensuring that the integrity of type constraints.
22.What is Type Erasure in Java Generics?
Type erasure is the process whereby Java removes generic type information during compilation to be compatible with earlier versions of Java that do not support generics. When the Java compiler processes generics, it replaces type parameters such as T or E with their bounds or Object if no bounds are declared.
For example,
If no bounds are declared, a generic class List is treated at runtime as List<Object>.
If bounds are specified, such as T extends Number, then the compiler replaces T with Number.
This is what an example looks like:
List<Integer> list = new ArrayList<>();
At runtime this becomes
List list = new ArrayList();
Key takeaways about type erasure
Only raw types are present at runtime. Type parameters are not accessible as generic types.
Casting is internally applied to ensure type safety while accessing elements from a collection.
Type erasure ensures that the compiled code using generics can interoperate with legacy code that uses raw types.
Although type erasure simplifies runtime type handling, it imposes some restrictions, such as not being able to use primitives directly in generics or preserve type information during execution.
23.What is the Use of the extends T> Wildcard in Java?
The <? extends T> generic type in Java declares the type which must be either T or any of T's subclasses. You can use where you desire to work with methods or collections over a range of related types without knowing the exact type.
Example Use Case:
Assume you have a method which processes a list of numbers:
public void processNumbers(List<? extends Number> numbers){
for (Number num : numbers) {
System.out.println(num);
}
}
In this example
The method can take a List of Integer, List of Double or any other list whose elements are subclasses of Number.
The method is only allowed to read elements since the exact type of the elements in the list is unknown, so it cannot append new elements.
Important Points:
It offers more flexibility to generic programming.
The primary aim is to allow reading elements from a collection with different but type-related types.
The restriction that it can accept an element from adding null type safe ensures that nothing operates on that would break integrity about the types that make up collection.
With the use of extends T > methods and classes could not only be declared to be type safe but versatile also.
24.What is the <? super T> wildcard in Java used for?
The <? super T> wildcard in Java declares a generic type of any type T and all of its supertypes. It can be really handy when you add elements to some collection and you are sure you are not going to read them by using some particular type. This means it provides the needed flexibility while processing generic types but type safety is preserved.
Example:
Declaration:
List<? super Integer> list;
Here, the list may be considered a collection of Integer or any of its supertypes, such as Number or Object. Then you could add the Integer elements to the list as well as elements of any subclass of Integer. The reading of elements from the list is done by casting since you do not know what the type of the elements exactly is.
Key Points:
Adding Elements: You may add safely elements of type T or its subtypes to the collection.
list.add(10); // Ok
list.add(new Integer(20)); // Ok
Reading Elements: Reading elements is not type-safe unless you make explicit use of casting, since the wildcard only guarantees that the list contains elements of type T and its supertypes.
Object obj = list.get(0); /* Legal as everything is an Object */
Integer num = (Integer) list.get(0); // Needs casting
The <? super T> wildcard is extremely useful in situations such as consumer APIs where elements are consumed-for example, added-but not necessarily read with a specific type.
25.Can You Modify the Elements in a List<? extends T>? Why or Why Not?
No, you can't change the elements of a List<? extends T>. It's because of the way Java guarantees type safety by using generics. With an upper bounded wildcard such as <? extends T>, you don't really know at compile time what the type of elements in the list is. It is a constraint that no incompatible type will inadvertently be added to the collection.
Explanation:
Here is the declaration:
List<? extends Number> list;
Here, list could be List<Integer>, List<Double> or any other type that extends Number. Adding elements to the list is disallowed since the compiler cannot know the actual type at runtime and maintain type safety.
Example:
List<? extends Number> list = new ArrayList<>();
list.add(10); // Compile-time error
list.add(3.14); // Compile-time error
The only things you can safely call are get methods on elements of the list. These will be returned as instances of the upper bound, in this case Number or any of its supertypes:
Number num = list.get(0); // Safe
Object obj = list.get(0); // Safe
Key Points:
Read-Only Nature: The primary purpose of <? extends T> is to let you read the elements while having flexibility in what types of lists you can pass to a method.
Adding Restricted: Since we do not know the exact type of the elements adding elements is not type-safe and therefore disallowed.
In summary,
List<? extends T> is read only for type safety reasons: you can read elements without any fear of their subclass but are prevented from modification in order not to cause any compatibility issues.
26.How Do Generics Relate to Type Safety in Java?
The use of generics is of extreme importance to increase type safety in Java. This is because the developer is allowed to mention exactly what kind of objects a collection or a generic structure is able to work with, allowing the compiler to check for the correctness of types during compile time and not at run time, which helps in decreasing runtime errors because of incorrect type casting.
Before generics were invented, collections could hold any type of object. This freedom often caused problems when trying to retrieve elements, developers had to cast the returned object to the expected type explicitly, which led to a ClassCastException at runtime.
For instance,
List list = new ArrayList();
list.add("Hello");
list.add(10); /* OK, but causes problems later */
String str = (String) list.get(1); /* Runtime error: ClassCastException */
You define what type a collection can hold in generics: for example, List<String>.
Then you make sure only objects of that type can be added to a collection. This way the compiler will report the violation in the compile-time itself:
List<String> list = new ArrayList<>();
list.add("Hello");
// list.add(10); /* Compile-time error */
String str = list.get(0); /* No casting needed */
By enforcing type safety at compile time, generics minimize the chance of runtime errors and make the code more robust while improving readability and maintainability.
27.How Does Type Inference Work in Java Generics?
Type inference in Java generics refers to the compiler's ability to deduce the type of a generic parameter automatically, based on the context in which it is used. This feature simplifies the syntax by eliminating the need for explicit type declarations in certain situations, while still preserving type safety
.
For instance, when creating a generic collection, you can omit the type on the right hand side of the assignment:
List<String> list = new ArrayList<>();
The compiler will understand, here that it needs to contain the string-type objects since the variable list has a declaration type: List<String>. This has diminished the repetition that occurs within this line of code hence making the coding clean while yet being secure at the same time.
It has also commonly utilized in the application of generic method. An illustration of it,
public static void printElements (List < T > list )
{
for ( T element : list) {
System.out.println(element;
}
}
When calling the method, the compiler infers the type parameter based on the arguments provided:
List<Integer> numbers = List.of(1, 2, 3);
printElements(numbers); // Type T is inferred as Integer
By simplifying the code and reducing verbosity, type inference improves developer productivity while maintaining strict type checking.
28.Can You Use Wildcards in Method Arguments with Generics?
Wildcards are applicable on the parameters of the method that help to enhance flexibility for a generic method signatures. This enables the acceptance of various types and eliminates any fixed or strict form to allow reuse in many aspects.
The common type of wildcards that have three variants.
Unbounded Wildcard(?)
Used when representing the unknown type.
The given is one such example that states-
public void processList(List<?> list) {
for (Object obj : list)
System.out.println(obj);
}
This way, the method can accept a List of any type, like List<String>, List<Integer> etc. However adding elements to the list is not permitted because the type is unknown.
Upper-Bounded Wildcard (? extends T)
It restricts the type to T or its subclasses. For example:
public void sumNumbers(List<? extends Number> list) {
double sum = 0;
for (Number num : list) {
sum += num.doubleValue();
}
System.out.println("Sum: " + sum);
}
This enables the method to accept lists of Number or its subclasses, such as Integer and Double. You can read elements as Number but cannot add new elements.
Lower-Bounded Wildcard (? super T)
This would restrict the type to T or its supertypes. For example:
public void addIntegers(List<? super Integer> list) {
list.add(10);
list.add(20);
}
This helps in allowing the method to accept a list of Integer or its supertypes, such as Number or Object. You can add Integer elements but cannot safely read them as a specific type without casting.
Wildcards make methods adaptable to a wider variety of use cases while preserving type safety, ensuring that generic code is both flexible and robust.
Previous Topic==> Java Features FAQ. || Next Topics==> Inner and Anonymous classes Java FAQ
Other Topic==>
Banking Account Management Case Study in SQL
Top SQL Interview Questions
Employee Salary Management SQL FAQ!. C FAQ
Top 25 PL/SQL Interview Questions
Joins With Group by Having
Equi Join
Joins with Subqueries
Self Join
Outer Join