I understand how ? extends .. and ? super .. work on their own and which generic types are possible, but I just can't understand how the following is possible with this hierarchy:
-> means extends
Classes are X (lowest), A to E (highest)
Interface is F
X -> A (implements F) -> B -> C -> E (implements F)
also D -> E
public class Node<T extends ClassE> {
private T info;
public T getInfo() {
return info;
}
public void setInfo(T info) {
this.info = info;
}
}
public static void main (String [] args){
Node<? super ClassB> n2 = new Node<ClassC>();
// this makes sense, since Node accepts below E and above B
InterfaceF i2 = n2.getInfo();
// how? Not only outside of <? extends E> but also getting value even though
// <? super B> is defined above, what's up with PECS?
n2.setInfo(new ClassX());
// also.. how? I'm setting a class that's out of the allowed range +
// seemingly violating the PECS for <? extends E>
}
As you can see, I'm totally confused when it comes to combining them and it's quite surprising for me having those declarations pass the compiler without problems.
I read somewhere that a combination of both bounds isn't possible in Java, but how does that work then?
The first line InterfaceF i2 = n2.getInfo(); compiles because the lower-bounded wildcard still retains the bound on the type variable itself. Since the type variable has an upper bound ClassE, getInfo() still returns a ClassE. Since ClassE implements InterfaceF, the assignment compiles.
In other words, we could imagine that when you did Node<? super ClassB> you implicitly actually did something like (made-up syntax) Node<? extends ClassE & super ClassB>. The type argument to the Node is both a supertype of ClassB and a subtype of ClassE.
This is similar to how Node<?> is implicitly the same as Node<? extends ClassE>.
The way this is actually specified is a little bit complicated, but it's in capture conversion. Capture conversion is the process whereby the compiler takes a type with wildcards and treats it as if it was a type without wildcards, for the purpose of determining subtyping.
Let G name a generic type declaration with n type parameters A1,...,An with corresponding bounds U1,...,Un.
There exists a capture conversion from a parameterized type G<T1,...,Tn> to a parameterized type G<S1,...,Sn, where, for 1 ≤ i ≤ n :
[...]
If Ti is a wildcard type argument of the form ? super Bi, then Si is a fresh type variable whose upper bound is Ui[A1:=S1,...,An:=Sn] and whose lower bound is Bi.
In other words, Si (the type argument after capture conversion corresponding to ? super ClassB) gains its lower bound from the bound of the wildcard and its upper bound from the bound of the type variable declaration.
The second line n2.setInfo(new ClassX()); compiles because ClassX is a subclass of ClassB so it's implicitly convertible to it. We could imagine that n2 was a Node<ClassB> and it might be more obvious why this line compiles:
Node<ClassB> n2 = ...;
n2.setInfo(new ClassX());
setInfo accepts ClassB as well as any subtype of ClassB.
Also, with respect to this:
I read somewhere that a combination of both bounds isn't possible in Java, but how does that work then?
The type system in the compiler does a lot of things that we can't explicitly do ourselves. Another good example (although unrelated) is type inference with anonymous classes:
int num = Objects.requireNonNull(new Object() {int num = 42;}).num;
System.out.println(num); // 42
It compiles because type inference is allowed to infer that the type argument to T of requireNonNull is the anonymous object type, even though we could never provide that type as an explicit type argument ourselves.
Disclaimer: I do not know the concrete type inference rules but explain to my best understanding.
Regarding InterfaceF i2 = n2.getInfo() - since Node<T extends E> it is assured that Node.getInfo() returns something extends E. According to your description E implements F. As such it is ensured that T extends E will also implement F. Therefore Node.getInfo() = T extends E implements F. So n2.getInfo() implements F is fine.
Regarding n2.setInfo(new ClassX()) - I do not have a formal-like explanation for this as above but let's try to think about it: Basically you Node<? super ClassB> tells everyone to expect at most ClassB as its lowest for the Node's content. However, since ClassX transitively inherits ClassB it is perfectly valid since it will satisfy all interface guarantees posed by ? super ClassB.
Hope this helps!
public class Test {
public interface F {}
public static class E implements F {}
public static class D extends E {}
public static class C extends E {}
public static class B extends C {}
public static class A extends B implements F {}
public static class X extends A {}
public static class Node<T extends E> {
private T info;
public T getInfo() {return info;}
public void setInfo(T info) {this.info = info;}
}
public static void main(String[] args) {
Node<? super B> n = new Node<C>(); // C is superclass of B = OK
F i = n.getInfo(); // node type = B|C|E all these types implements F (since E implements F) = OK
n.setInfo(new X()); // X has supertypes A,B,C,E = can be casted to B and so satisfy <? super B>
}
}
Related
Case 1:
class Gen3<T extends Number> {
T val;
<T extends String> Gen3(String ob){
}
}
Here compiler doesn't give any error, but it should give right? Because here are two contradicting bounds for T. Please help me in understanding this.
Case 2:
class Gen3<T extends Number> {
T val;
<T extends String> Gen3(String ob) {
}
}
Suppose if I write following to test the above class
Gen<Integer> a = new Gen<>("r");
Now how automatic type inference would work here?
Please help in understanding this.
There are no two contradicting bounds of T. There are two type variables that happen to have the same name. In the constructor, the type parameter T hides the type parameter of the class level.
Note that the issue is not with the different type bounds. If you actually try to do something with the type parameter, such as:
class Gen3<T extends Number> {
T val;
<T extends Number> Gen3(T ob) {
val = ob;
}
}
This won't pass compilation even if both Ts have the same type bound, since the type parameter of ob is different than the type parameter of val.
"Because here are two contradicting bounds for T" - no. There simply a two separate definitions of T that have nothing to with each other. The T on the constructor hides the T of the class, same with local variables vs. fields of the same name. A "proper" IDE will tell you that the inner T hides the outer T and is unused.
This "use case" of Generics doesn't make sense in multiple aspects. With the <T extends String> clause you introduce a type variable that you don't use and don't give the compiler a chance to replace it with a concrete type in a given call situation.
Your definition is equivalent to the following (I just renamed the two different type variables to have different names, making the discussion easier):
class Gen3<T extends Number> {
T val;
<U extends String> Gen3(String ob) {
}
}
The <U extends String> clause tells the compiler: "The following constructor will use a type parameter U, and I only allow U to be String or a subclass of String". As others already said, String is final, so U can only be String, so it isn't really a variable type, and declaring a variable type that can't vary doesn't make sense. I'll continue with a modified version:
class Gen3<T extends Number> {
T val;
<U extends Collection> Gen3(String ob) {
}
}
If you do Gen<Integer> a=new Gen<Integer>("r");, how should the compiler find out the concrete class to replace U with? The <Integer> part applies to the T variable, so it doesn't help for U. As you don't refer to U in any of the arguments, there's no hint for the compiler.
The idea of Generics is that a class has some elements where you want to allow for varying types, and allow the compiler to flag misuse, e.g. add an Integer to a List<String>:
List<String> myList = new ArrayList<String>();
myList.add(new Integer(12345));
Here, the compiler can match the generic List<E> type parameter E to be a String (from the List<String> declaration). In this context, the gegeric List.add(E e) method declaration becomes an add(String e), and doesn't match the usage with new Integer(12345), which isn't a String, allowing the compiler to flag the error.
Summary:
Introduce a type parameter only if you give the compiler a chance to deduce it from the call arguments.
All,
To test the Java Generics Type Inference I created below sample code.
public static <U> U addBox2(U u1, U u2) {
return u2;
}
static interface A {}
static interface B {}
static class S {}
static class C extends S implements A,B {}
static class D extends S implements B,A {}
If you see above A and S have no relationship.
In the code below -
A a = addBox2(new C(),new D());
I was hoping to receive the compilation error since the type inferred was S and I am assigning it to A and A and S has no relationship still this worked just fine.
Can someone help me explain why this behavior?
As requested:
A and S don't have a relationship indeed but since you pass a C and D to the method and both implement A (and B) they have something in common, i.e. they both are As.
That means the following would work:
A a = addBox2(new C(),new D()); //common type is A
B b = addBox2(new C(),new D()); //common type is B
S s = addBox2(new C(),new D()); //common type is S
Object o = addBox2(new C(),new D()); //common type is Object
As long as type inference can solve the generic type from the assignment as well as the parameters it should work (note that type inference isn't as good in Java versions prior to 8, especially in 5 and 6).
You can, however, define the type yourself by passing it to the method call:
A a = EnclosingClass.<S>addBox2(new C(),new D()); //static method within EnclosingClass
A a = this.<S>addBox2(new C(),new D()); //instance method
In both cases you define the generic type to be S and thus the assignment won't work.
Type parameter U is inferred from the type of the a variable you are assigning the result of addBox2(...) to, which in your case is A. As both C and D implement A it works flawlessly.
The following of course won't work:
S s = addBox2(new C(),new D());
A a = s; // compile error, type mismatch
I suspect Eclipse and IntelliJ aren't annotating these properly. With this (slightly tweaked) example:
public static <U> U getU(U u1, U u2) {
return u2;
}
static interface A {}
static class S {}
static class C extends S implements A {}
static class D extends S implements A {}
static class T implements A {}
public static void main(String[] args) {
A a = getU(new C(), new D());
A b = getU(new C(), new T());
}
I see the following when I mouse-over the first call to getU() in main():
<? extends S> ? extends S analysis.Delme.getU(? extends S u1, ? extends S u2)
Which is clearly not correct - A does not extend S. I suspect Eclipse is mistakenly oversimplifying the generics here, and preferring to report that C and D both extend from S, rather than both implement A.
If I mouse-over the second call I instead get the much more reasonable:
<A> A analysis.Delme.getU(A u1, A u2)
Which is presumably what the compiler is actually doing here for both calls.
Note as Thomas points out, even if two classes have nothing in common (such as S and T) they still extend from Object, so Object c = getU(new S(), new T()); is valid.
Don’t trust the tooltip of an editor that might not cope with complex generic constructs. The inferred type is S & A & B (before Java 8), which can be assigned to A. In Java 8, the inferred type is just A, as generic method invocations are so-called poly expressions using the target type. For your addBox invocation, it makes no difference.
To illustrate the issue, you may write
Object o = Arrays.asList(new C(), new D());
in Eclipse using Java 7 compliance mode, and hover the mouse over asList. It will print
<? extends S> List<? extends S> java.util.Arrays.asList(? extends S... a), similar to the addBox invocation of your question, but when you change the code to
List<A> list = Arrays.asList(new C(), new D());
you get the compiler error “Type mismatch: cannot convert from List<S&A&B> to List<A>” (still in Java 7 mode), showing that the actual inferred type is S & A & B, not ? extends S.
When you turn the language compliance level to Java 8, the compiler error will go away, as in Java 8, the target type will be used to infer the type, which will be A instead of S & A & B.
As said, for your addBox invocation, it makes no difference whether A or S & A & B is inferred, as both can be assigned to A, so it works under both, Java 7 and Java 8 compliance level. But when you use an invocation, where it makes a difference, like with Arrays.asList, you can see the actually inferred type, which is never ? extends S as the editor’s tooltip claims…
I've come across error with what I thought would have been a straight forward deduction where a method using generics specifies a version of a generic class as one parameter and for a second parameter it specifies something of the type used for the version of the previous parameter.
static class GenericClass<T0>
{
T0 getT()
{
return null;
}
static <T1> void func3( GenericClass<T1> a, T1 b )
{
}
}
void testcase( GenericClass<? extends Integer> a )
{
GenericClass.func3( a, a.getT() );
}
Unfortunately it shows as an error:
"The method func3(GenericClass<T1>, T1) in the type GenericClass is not applicable for the arguments (GenericClass<capture#6-of ? extends Integer>, capture#7-of ? extends Integer)"
However this change to the header of func3 works.
static <T1> void func3( GenericClass<? extends T1> a, T1 b )
and so does this change to the header of the testcase method.
void testcase( GenericClass<Integer> a )
Even if the testcase header doesn't give a concrete type for the class, the class specifies the return type will be the same as its generic type so why does the error message suggest they are potentially two different types?
This example isn't obvious because Integer is final, so let's take an example where Parent has two subclasses, Foo and Bar.
? extends Parent means some specific subtype of Parent. So GenericClass<Foo> and GenericClass<Bar> could be used where a GenericClass<? extends Parent> is expected.
Here
static <T1> void func3( GenericClass<T1> a, T1 b )
you have a generic method where the generic type parameter is used in both parameter declarations. The compiler must guarantee that the type is correct in both provided arguments during a method invocation.
Given this
GenericClass<? extends Parent> a = ...;
GenericClass.func3( a, a.getT() );
it has no way to do that. You must first understand that Java doesn't know that both those arguments come from the same source. All it looks at is their types.
The first argument is of type GenericClass<? extends Parent> while the second argument is of type ? extends Parent. Consider the following
GenericClass<? extends Parent> a = new GenericClass<Foo>(someFoo);
GenericClass<? extends Parent> b = new GenericClass<Bar>(someBar);
GenericClass.func3( a, b.get());
The two arguments have the exact same compile time type as the method call above, but it is easier to see why it would fail. It cannot guarantee that T will be bound to the same type for both arguments. It must therefore fail.
abstract class Type<K extends Number> {
abstract <K> void use1(Type<K> k); // Compiler error (Type parameter K is not within its bounds)
abstract <K> void use2(Type<? extends K> k); // fine
abstract <K> void use3(Type<? super K> k); // fine
}
The method generic type K shadows the class generic type K, so <K> doesn't match <K extends Number> in use1().The compiler doesn't know anything usefull about new generic type <K> in use2() and use3() but it is still legal to compile . Why <? extends K> (or <? super K>) match <K extends Number>?
The problem you have is that There are two K types. It may be clearer if you rename one.
abstract class Type<N extends Number> {
abstract <K extends Number> void use1(Type<K> k); // fine
abstract <K> void use2(Type<? extends K> k); // fine
abstract <K> void use3(Type<? super K> k); // fine
}
There are cases where you have to provide duplicate information the compiler can infer, and other places where you don't. In Java 7 it has added a <> diamond notation to tell the compiler to infer types it didn't previously.
To illustrate what I mean. Here is different ways to create an instance of a generic class. Some requires the type be given twice, others only once. The compiler can infer the type.
In general, Java doesn't infer types when it might do in most other languages.
class Type<N extends Number> {
private final Class<N> nClass;
Type(Class<N> nClass) {
this.nClass = nClass;
}
static <N extends Number> Type<N> create(Class<N> nClass) {
return new Type<N>(nClass);
}
static void main(String... args) {
// N type is required.
Type<Integer> t1 = new Type<Integer>(Integer.class);
// N type inferred in Java 7.
Type<Integer> t2 = new Type<>(Integer.class);
// type is optional
Type<Integer> t3 = Type.<Integer>create(Integer.class);
// type is inferred
Type<Integer> t4 = create(Integer.class);
}
When you define the method like this:
abstract <K> void use1(Type<K> k);
You're effectively hiding the type K in your class definition. You should be able to define the methods like this:
abstract void use1(Type<K> k);
First of all, let's rewrite it to avoid shadowing:
abstract class Type<N extends Number> {
abstract <K> void use1(Type<K> k);
abstract <K> void use2(Type<? extends K> k);
abstract <K> void use3(Type<? super K> k);
}
In the first method K acts as a type parameter of Type<N extends Number>, thus its value sould comply to the bound of Type's N. However, method declaration doesn't have any restrictions on value of K, therefore it's not legal. It would be legal if you add a necessary restriction on K:
abstract <K extends Number> void use1(Type<K> k);
In the following methods, the actual type parameter of Type is unknown (?), and K imposes additional bound on it, so that there is nothing illegal in these declarations.
Here is a more practical example with the similar declarations:
class MyList<N extends Number> extends ArrayList<N> {}
<K> void add1(MyList<K> a, K b) {
a.add(b); // Given the method declaration, this line is legal, but it
// violates type safety, since object of an arbitrary type K can be
// added to a list that expects Numbers
// Thus, declaration of this method is illegal
}
<K> void add2(MyList<? extends K> a, K b) {
// a.add(b) would be illegal inside this method, so that there is no way
// to violate type safety here, therefore declaration of this method is legal
}
<K> void add3(MyLisy<? super K> a, K b) {
a.add(b); // This line is legal, but it cannot violate type safey, since
// you cannot pass a list that doesn't expect K into this method
}
This is a gray area; javac 7 and 6 disagree; JLS3 is outdated; no idea where's the new spec.
What is the difference in following 2 lines?
public static <T extends Comparable<? super T>> int methodX(List<T> data)
public static <T> int methodX(List<? extends Comparable<? super T>> data)
Your first option is a "stricter" parametrisation. Meaning, you're defining the class T with a bunch of restrictions, and then use it later on with List. In your second method, the parameter class T is generic with no conditions, and the Lists class parameter is defined in terms of the parameter T.
The second way is syntactically different as well, with a ? instead of the first option's T, because in the parameter definition you aren't defining the type parameter T but rather using it, so the second method cannot be as specific.
The practical difference that comes out of this is one of inheritance. Your first method needs to be a type that is comparable to a super class of itself, whereas the second type need only be comparable to an unconditional/unrelated T:
public class Person implements Comparable<Number> {
#Override
public int compareTo(Number o) {
return 0;
}
public static <T extends Comparable<? super T>> int methodX(List<T> data) {
return 0;
}
public static <T> int methodY(List<? extends Comparable<? super T>> data) {
return 0;
}
public static void main(String[] args) {
methodX(new ArrayList<Person>()); // stricter ==> compilation error
methodY<Object>(new ArrayList<Person>());
}
}
If you change the Comparable of Person to be able to compare Object or Person (the inheritance tree of the base class) then methodX will also work.
To the callers, the 2nd version is roughly equivalent to
public static <T, X extends Comparable<? super T>> int methodX(List<X> data)
Suppose a caller calls it with an arg whose concrete type List<Foo>. Type inference will conclude that X=Foo. Then we get a new equation about T from X's bound
=>
Foo <: Comparable<? super T>
( A <: B means A is a subtype of B)
If Foo is Comparable at all, it almost certainly implements Comparable<Foo> [2]
=>
Comparable<Foo> <: Comparable<? super T>
=>
T <: Foo
Without further information, inference chooses T=Foo.
Therefore from caller's POV, the two versions are not really different.
Inside method body, the 2nd version does not have access to type parameter X, which is a synthetic one introduced in compilation phase. This means you can only read from data. Things like
X x = data.get(0);
data.set(1, x);
are impossible in version#2; No such problem in version #1 with T.
However we can forward #2 to #1
<T1> method1(List<T1> data){ data.set(...); }
<T2> method2(List<?...> data)
{
method1(data);
}
(they must have difference method names; overloading not allowed since java7)
This is because for the compiler, type of data is really List<X> (it knows the secrete X), so there is no problem calling method1(data) after inferring that T1=X
[1] JLS3, 5.1.10 Capture Conversion
[2] According to the javadoc of Comparable, This interface imposes a total ordering on the objects of each class that implements it. That means if Foo implements Comparable<W>, W must be Foo or a super type of Foo. It is quite improbably for a subclass implementation to define a total order among objects of a super class. So W most definitely should be Foo. Otherwise funny things would happen. The notorious example is 'Timestamp', its javadoc (now) explains why it can't be compared with its supertype Date
The first method expects a list of elements which can be compared against their own class or a supertype of it. Say, real numbers can be compared to any kind of numbers:
class Real extends Number implements Comparable<Number> {
public int compareTo(Number o) ...
}
A bit more restrictive, but still acceptable for your first method is the following:
class Real extends Number implements Comparable<Real> {
public int compareTo(Real o) ...
}
But the second method is actually not very different from this version:
public static int methodY(List<? extends Comparable<?>> data) ...
That is to say, you can replace T with an unnamed wildcard ? because it is used only once in the method signature. It does not use concepts like the same class or an object's own class, etc.