Универсальный метод Ковариантность - допустимое ограничение или недосмотр компилятора? - PullRequest
2 голосов
/ 16 ноября 2010

Кто-нибудь знает окончательный ответ на вопрос, почему java-компилятор не разрешает следующее:

class BaseClass {

      public <T extends Number> T getNumber(){
            return null;
      }
}

class SubClass extends BaseClass{

      @Override
      public <T extends Integer> T getNumber(){
            return null;
      }

}

это заставляет компилятор жаловаться на:

"Метод getNumber () типа SubClass должен переопределить метод суперкласса"

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

class BaseClass<T extends Number> {
      public T getNumber(){
            return null;

      }
}


class SubClass<T extends Integer> extends BaseClass<T>{
      @Override   
      public  T getNumber(){
            return null;

      }
}

Это может быть использовано неправильно, если подкласс вызывает супер реализацию, но компилятор выдает предупреждение об этом. Мой единственный вывод заключается в том, что это недосмотр компилятора со стороны людей из Sun (я не могу сказать, что Oracle: -S).

У кого-нибудь есть окончательный ответ на этот вопрос?

Ответы [ 4 ]

4 голосов
/ 16 ноября 2010

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

Тогда что, если у вашего класса также есть метод <T extends Number> void setNumber(T number)?

BaseClass foo = new SubClass();
long longValue = 42;
foo.<Long>setNumber(longValue); 

Вышеприведенное будет принято компилятором, поскольку BaseClass.setNumber принимает любой параметр типа, полученный из Number.Но фактический экземпляр принимает только целые числа!

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

C # 4.0 это фактически было решено ,но это включает в себя явную пометку параметров типа как ковариантных или контравариантных с помощью ключевых слов out и in. Это не было чем-то, что могло быть просто разрешено компилятором без изменения языка.

0 голосов
/ 16 ноября 2010

Потому что здесь нет ковариации.Ковариантность означает «меняется в зависимости от» и относится к типу возвращаемого значения, варьирующемуся как содержащий класс.Это не происходит здесь.В вашей ковариации нет «со».

0 голосов
/ 16 ноября 2010

@ Вим Коенен дал хороший ответ.

Вот небольшое разъяснение.

Если это скомпилируется, то чего бы вы ожидали, произошло:

0 голосов
/ 16 ноября 2010

Я не совсем понимаю вашу проблему:

Когда я реализовал вашу первую часть исходного кода и скомпилировал с использованием javac *.java, он скомпилировался нормально. Однако, если я изменил ваш SubClass.getNumber() метод, чтобы сделать это ...

/* (non-Javadoc)
  * @see constraint.base.BaseClass#getNumber()
  */
 @Override
 public <T extends Number> T getNumber() {
  // TODO Auto-generated method stub
  return super.getNumber();
 }

Я получаю следующую ошибку:

SubClass.java:18: type parameters of <T>T cannot be determined; no unique maximal instance exists for type variable T with upper bounds T,java.lang.Number
                return super.getNumber();
                                      ^
1 error

Причина приведенного выше кода заключается в том, что при вызове super.getNumber() компилятор не получает никакого ввода типа T, следовательно, он не может определить, что T будет возвращено super.getNumber() и, таким образом не может удовлетворить тип возврата с SubClass.getNumber()

Теперь, чтобы получить ошибку от компилятора, "The method getNumber() of type SubClass must override a superclass method", ваш BaseClass должен был быть abstract вместе с его getNumber() методом.

Что касается второго предоставленного вами кода, подкласс может, по сути, вызвать super.getNumber(), так как компилятор будет знать (при компиляции), что тип общего параметра подкласса выводится для типа параметра BaseClass.

Кроме того, я действительно не знал, что вы пытаетесь спросить.

...