Подстановочные знаки Java по умолчанию равны java.lang.Object вместо верхней границы

Учитывая код

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.
        });
    }
}

Почему универсальный тип класса Test2 не по умолчанию для класса Base но вместо этого по умолчанию класс java.lang.Object?

Я предоставил верхнюю границу, но, похоже, это не имеет значения, если используются подстановочные знаки или когда вообще не указывается универсальный символ.

Код в основной функции должен быть скомпилирован, если вы спросите меня.

2 ответа

Решение

Объявив Test2 класс без указания аргумента типа:

class Test2 extends Simple {}

вы создаете необработанный тип. Поэтому компилятор рассматривает это как экземпляр Object,

Это не "по умолчанию Object".

На самом деле произошло то, что с помощью необработанного типа (Test2 расширяет сырой тип Simple), он "отключает" все дженерики для этого типа. Так Test2"s addOnSomethingListener метод (который унаследован от необработанного типа Simple) на самом деле имеет подпись void addOnSomethingListener(Consumer action) (т.е. тип параметра стирается в необработанный тип Consumer).

Так уж получилось, что сырой тип Consumer"s accept метод имеет подпись void accept(Object) (потому что это нормально void accept(T), но T в Consumer неограничен, поэтому при использовании необработанного типа Consumerстирается void accept(Object),

Другие вопросы по тегам