Wietlol
Wietlol

Reputation: 1071

Java wildcards defaulted to java.lang.Object instead of upper bound

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

Answers (2)

newacct
newacct

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

syntagma
syntagma

Reputation: 24344

By declaring Test2 class without specyfing its type argument:

class Test2 extends Simple {}

you are creating a raw type. Therefore the compiler treats it as an instance of Object.

Upvotes: 4

Related Questions