Reputation: 1071
Given the code
abstract class Base<Thistype extends Base>
{
public void addOnSomethingListener(Consumer<? super Thistype> action)
{}
public void foo()
{
System.out.println("foo");
}
}
class Simple<Thistype extends Simple> extends Base<Thistype>
{
}
class Test1 extends Simple<Test1>
{
}
class Test2 extends Simple
{
}
class Test
{
public static void main(String[] args)
{
Test1 test1 = new Test1();
Test2 test2 = new Test2();
test1.addOnSomethingListener(test ->
{
test.foo(); // VALID as "test" is of type "Thistype" which is "Test1".
});
test2.addOnSomethingListener(test ->
{
test.foo(); // INVALID as "test" is of type "Thistype" which is "java.lang.Object" instead of "Base" which is the upper bound.
});
}
}
Why is the generic type of class Test2
not defaulted to class Base
but instead defaulted to class java.lang.Object
?
I provided the upper bound, but it seems that that is irrelevant if wildcards are used or when no generic is given at all.
The code in the main function should be able to be compiled if you ask me.
Upvotes: 3
Views: 180
Reputation: 122489
It does not "default to Object
".
What actually happened is that by using a raw type (Test2
extends the raw type Simple
), it "turns off" all generics for that type. So Test2
's addOnSomethingListener
method (which is inherited from the raw type Simple
) actually has signature void addOnSomethingListener(Consumer action)
(i.e. the parameter's type is erased to the raw type Consumer
).
It just so happens that the raw type Consumer
's accept
method has signature void accept(Object)
(because it's normally void accept(T)
, but the T
in Consumer
is unbounded, so when using the raw type Consumer
, it gets erased to void accept(Object)
.
Upvotes: 0