Well, its not as if Java7 is happy to run it. It gives couple of warnings before giving the error:
jatin@jatin-~$ javac -Xlint:unchecked -source 1.7 com/company/Main.java
warning: [options] bootstrap class path not set in conjunction with -source 1.7
com/company/Main.java:19: error: no suitable method found for set(Derived,Base)
set(new Derived(), new Consumer().get());
^
method Consumer.set(Base,Derived) is not applicable
(argument mismatch; Base cannot be converted to Derived)
method Consumer.set(Derived,Collection<? extends Consumer>) is not applicable
(argument mismatch; Base cannot be converted to Collection<? extends Consumer>)
com/company/Main.java:28: warning: [unchecked] unchecked cast
return (T) new Derived();
^
required: T
found: Derived
where T is a type-variable:
T extends Base declared in method <T>get()
The issue is this:
set(new Derived(), new Consumer().get());
When we do new Consumer().get()
, if we look at signature of get
. It returns us a Type T
. We know that T
somehow extends Base
. But we do not know what T
is specifically. It can be anything. So if we cant specifically decide what T
is, then how can compiler?
One way to tell compiler, is by hardcoding and telling it specifically by: set(new Derived(), new Consumer().<Derived>get());
.
The reason above is extremely extremely (repeated intentionally) dangerous, is when you try doing this:
class NewDerived extends Base {
public String getName(){return "name";};
}
NewDerived d = new Consumer().<NewDerived>get();
System.out.println(d.getName());
In Java7 (or any Java version), it will throw exception at runtime:
Exception in thread "main" java.lang.ClassCastException:
com.company.Derived cannot be cast to com.company.NewDerived
Because get
returns an object of type Derived
but you have mentioned to compiler that it is of NewDerived
. And it cant convert Derived to NewDerived rightly. This is why it shows a warning.
As per the error, now we understand what is wrong with new Consumer().get()
. It's type is something that extends base
. Doing set(new Derived(), new Consumer().get());
looks for a method that takes arguments as Derived (or any super class of it), something that extends Base
.
Now both of your methods qualify for the first argument. As per the second argument something that extends base
, again both are eligible as that something can be Derived or extend Derived or extend Collection. This is why Java8 throws Error.
As per Java7, its type inference is bit weaker. So it tries doing something similar,
Base base = new Consumer().get();
set(new Derived(), base);
And again, it cannot find the right method which takes Derived, Base
as arguments. Hence it throws error but for different reason:
set(new Derived(), new Consumer().get());
^
method Consumer.set(Base,Derived) is not applicable
(argument mismatch; Base cannot be converted to Derived)
method Consumer.set(Derived,Collection<? extends Consumer>) is not applicabl e
(argument mismatch; Base cannot be converted to Collection<? extends Consu mer>)
PS: Thanks To Holger, for pointing out incompleteness of my answer.
javac
cannot. Is it safe to assume you're usingjavac
? – Ezarra