Generic Interfaces in Java are the interfaces that deal with abstract data types. Interface help in the independent manipulation of java collections from representation details. They are used to achieving multiple inheritance in java forming hierarchies. They differ from the java class.
Even though the Java collection classes have been modified to take advantage of generics, you are not required to specify type parameters to use them. A generic type used without type parameters is known as a raw type .
A Generic Version of the Box Class To update the Box class to use generics, you create a generic type declaration by changing the code "public class Box" to "public class Box<T>". This introduces the type variable, T, that can be used anywhere inside the class.
A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.
CharSequence
is an interface
. Therefore even if SomeClass
does not implement CharSequence
it would be perfectly possible to create a class
class SubClass extends SomeClass implements CharSequence
Therefore you can write
SomeClass c = getCharSequence();
because the inferred type X
is the intersection type SomeClass & CharSequence
.
This is a bit odd in the case of Integer
because Integer
is final, but final
doesn't play any role in these rules. For example you can write
<T extends Integer & CharSequence>
On the other hand, String
is not an interface
, so it would be impossible to extend SomeClass
to get a subtype of String
, because java does not support multiple-inheritance for classes.
With the List
example, you need to remember that generics are neither covariant nor contravariant. This means that if X
is a subtype of Y
, List<X>
is neither a subtype nor a supertype of List<Y>
. Since Integer
does not implement CharSequence
, you cannot use List<Integer>
in your doCharSequence
method.
You can, however get this to compile
<T extends Integer & CharSequence> void foo(List<T> list) {
doCharSequence(list);
}
If you have a method that returns a List<T>
like this:
static <T extends CharSequence> List<T> foo()
you can do
List<? extends Integer> list = foo();
Again, this is because the inferred type is Integer & CharSequence
and this is a subtype of Integer
.
Intersection types occur implicitly when you specify multiple bounds (e.g. <T extends SomeClass & CharSequence>
).
For further information, here is the part of the JLS where it explains how type bounds work. You can include multiple interfaces, e.g.
<T extends String & CharSequence & List & Comparator>
but only the first bound may be a non-interface.
The type that is inferred by your compiler prior to the assignment for X
is Integer & CharSequence
. This type feels weird, because Integer
is final, but it's a perfectly valid type in Java. It is then cast to Integer
, which is perfectly OK.
There is exactly one possible value for the Integer & CharSequence
type: null
. With the following implementation:
<X extends CharSequence> X getCharSequence() {
return null;
}
The following assignment will work:
Integer x = getCharSequence();
Because of this possible value, there's no reason why the assignment should be wrong, even if it is obviously useless. A warning would be useful.
In fact, I've recently blogged about this API design anti pattern. You should (almost) never design a generic method to return arbitrary types because you can (almost) never guarantee that the inferred type will be delivered. An exception are methods like Collections.emptyList()
, in case of which the emptiness of the list (and generic type erasure) is the reason why any inference for <T>
will work:
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With