Generics - не может вернуть базу для T с T расширяет базу - PullRequest
2 голосов
/ 16 марта 2019
public class Testing<T extends Tuple> {

   public T method(){
     return new Tuple(); //Syntax error
   }
}

Вот ошибка, которую я получаю (синтаксическая ошибка):

Incompatible Types:
Required: T
Found: com.company.package1.Tuple

Несмотря на то, что я написал T extends Tuple в начале, почему я получаю эту ошибку?

Спасибо

Ответы [ 6 ]

2 голосов
/ 16 марта 2019

Объяснение

Вводя общий параметр T, вы даете пользователю своего класса возможность установить тип для вас.

Обобщения действуют так же, как параметры для методов, но параметры типа для классов .

extends Tuple является ограничением для пользователя. Таким образом, ваш пользователь может использовать IntTuple, например. Ваш метод говорит, что возвращает T, но на самом деле возвращает только Tuple. Когда пользователь устанавливает общий параметр на IntTuple, метод также должен будет возвращать IntTuple, а не Tuple.


Пример

Рассмотрим следующий, немного более читаемый пример:

public class Farm<A extends Animal> {
    public A produce() {
        // ...
    }
}

Пользователь может использовать ферму следующим образом:

Farm<Pig> pigFarm = new Farm<>();

Pig pig = produce();

Важной частью здесь является то, что produce сейчас должны выдавать Pig с. И если у другого пользователя есть Farm<Cow>, эта ферма должна выдать Cow с.

Однако ваша текущая реализация была:

public A produce() {
    return new Animal();
}

Но Animal - это , а не Pig или Cow. Так что не может работать. Он будет (теоретически) работать только в том случае, если пользователь (по какой-либо причине) будет использовать Farm<Animal>.


Решение

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

Может быть, вы вообще не хотели генериков и просто возвращаете Animal, отбрасывая T. Возможно, вы действительно хотите вернуть T, но захватите фактический T экземпляр из другого места. Создание нового экземпляра T с помощью этого метода будет довольно трудным, поскольку вы не знаете точный тип T, который будет установлен пользователем. Cow может иметь конструкторы, отличные от Pig.


Примечание

Другие ответы предлагают привести к T, но это, скорее всего, не даст того, что вы намеревались сделать. Это способ сделать этот код каким-то образом компилируемым. Затем он будет поддерживать Tuple, но без подклассов. В примере это отражает Farm<Animal>. И он потерпит неудачу с ClassCastException для всех других входов. Поскольку Animal нельзя преобразовать в Pig и т. Д.

0 голосов
/ 16 марта 2019

То, что T расширяется Tuple, не означает, что Tuple имеет тип T.

0 голосов
/ 16 марта 2019

@ Андреас в первом комментарии прав. Вы должны попросить его написать свой комментарий и принять его.

Преобразование родителя в подкласс не будет работать.

Просто чтобы прояснить ситуацию, посмотрите на этот пример:

public class Tuple {
}

public class NamedTuple extends Tuple {
    public String getName() {
        return "NamedTuple";
    }
}

public class Testing<T extends Tuple> {
    public T method(){
        return (T) new Tuple(); // asking for trouble
    }
}

public static void main(String[] args) {
    final NamedTuple namedTuple = new Testing<NamedTuple>().method();

    // what exactly can be expected here, given that method() creates a Tuple?
    namedTuple.getName();
}

Что должно произойти, когда вы звоните namedTuple.getName()? Testing.method() создал экземпляр Tuple, но Tuple не имеет метода getName(), ожидаемого от класса NamedTuple.

К счастью, этого никогда не произойдет, поскольку method() потерпит неудачу с ClassCastException.

0 голосов
/ 16 марта 2019

Чтобы исправить эту ошибку, вы должны изменить свой код следующим образом:

public class Testing<T extends Tuple> {
     @SuppressWarnings({"unchecked"})
     public T method(){
          return (T) new Tuple();
     }
0 голосов
/ 16 марта 2019

Вы должны привести к T, потому что метод method () должен вернуть объект класса типа T

 class Testing<T extends Tuple> {

    public T method(){
        return (T) new Tuple()
    }
}
0 голосов
/ 16 марта 2019

Вы получаете ошибку компиляции, поскольку вы возвращаете Tuple из метода, который имеет T в качестве возвращаемого типа.Во время выполнения это T может быть не Tuple, а каким-то типом, расширяющим его.Вы можете привести его к T:

return (T) new Tuple();

После стирания типа оно становится равным return (Tuple) new Tuple();, поскольку Tuple связано с T.Вот почему он будет скомпилирован без ошибок.

Но он будет рассматриваться как Unchecked cast и выдает предупреждение.Это может привести к ClassCastException во время выполнения, когда вместо Tuple будет некоторый подкласс Tuple.

...