Подтвердить что ты не робот

Java Generics - метод переопределения

У меня есть пара классов ClassA и ClassB, как показано ниже.
случай 1:

class ClassA<T extends Number>{
    void method(T t){}
}

class ClassB extends ClassA<Integer>{
    @Override
    void method(Integer i){}
}

случай 2:

class ClassA{
    void method(Number t){}
}

class ClassB extends ClassA{
    @Override
    void method(Integer i){}
}

У меня здесь два вопроса.
[Q1] Правильно ли, если я скажу, что case2 является представлением среды выполнения case1 (после стирания типа)?

[д2] Если я прав [q1], то почему case1 принимается как действительное переопределение? (Я знаю, почему case2 не является допустимым переопределением, поскольку параметры не совпадают.)

Кто-то, пожалуйста, пролил свет на это. Спасибо заранее.

4b9b3361

Ответ 1

Ответ на [q1] - нет. Компилятор будет генерировать мостовой метод в ClassB, который будет фактически превышать method(Number).

class ClassB extends ClassA{
    // bridge method
    void method(Number i){
        method((Integer)i);
    }

    void method(Integer i){}
}

У вас будет полный ответ в java doc по стиранию типа.

Ответ 2

В Java (начиная с версии 5) возвращаемые типы переопределенных методов должны быть ковариантными, а параметры переопределенных методов должны быть контравариантными.

Это означает, что класс переопределения может быть более конкретным в том, что он возвращает, и принимать больше за то, что он получает.

Во втором примере представьте себе переменную типа ClassA с экземпляром ClassB в качестве значения.

 ClassA a = new ClassB(); // This is legal, since ClassB is a subclass of ClassA
 a.method(1.0); // This is legal, since ClassA.method accepts Number

Другой способ, однако, был бы в порядке:

public class ClassC { public Number method(Integer i) {...} }
public class ClassD extends ClassC {
   @Override 
   public Integer method(Number n) {...}
}

действителен, поскольку ClassD все еще выполняет контракт, определенный ClassC.

Ответ 3

Фактический суперкласс ClassB равен ClassA<Integer>. Поэтому его функция-член method имеет подпись времени компиляции:

void method(Integer t){}

Вы можете убедить себя, вызвав что-то вроде

ClassA a = new ClassB();
a.method(1.0);

Вы должны увидеть ошибку времени выполнения. На самом деле это можно скомпилировать, потому что я использовал стертую версию ClassA. Фактически каждое присвоение общий тип, отличный от ClassA<Integer> (например, ClassA<Number > ), потерпит неудачу из-за несовместимых типов.