2010-11-10 2 views
1

следующий код имеет ошибки компиляции в строке t3:автоматического связывания (тип вывода) родовых типов компилятором

public <E> List<E> getList() 
{ 
    return new ArrayList<E>(); 
} 
public <T> void first() 
{ 
    List<T> ret = new ArrayList<T>(); 
    List<T> list = getList(); 
    T t1 = ret.get(0); 
    T t2 = list.get(0); 
    T t3 = getList().get(0); 
} 

Сообщение об ошибке: Несоответствие типов: невозможно преобразовать из объекта в T

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

Редактировать: добавлено сообщение об ошибке.

Редактировать: добавлен еще один пример того, как ошибка не произошла.

Редактировать: удалил второй пример, поскольку он был запутанным, поставил вопрос более ясным.

+1

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

+0

Это поможет, если вы сообщите нам точное сообщение об ошибке. – thecoop

+0

нашел аналогичный вопрос с частичным объяснением: http://stackoverflow.com/questions/2055352/why-implicit-type-inference-only-works-in-an-assignment – oshai

ответ

5

В первом случае у вас есть два общих методы с параметрами типа именем T, но эти параметры типа различны, так что давайте назначать разные имена для них:

public <E> List<E> getList() { ... } 
public <T> void first() { ... } 

Тогда это работает следующим образом:

  1. элемент List<T> (который является объектом типа T) присваивается переменной типа T, так что все работает отлично:

    List<T> ret = new ArrayList<T>(); 
    T t1 = ret.get(0); 
    
  2. Во-первых, объект типа List<E> присваивается List<T>. Это утверждение отлично работает, поскольку параметр типа E выведен из типа левой стороны присвоения, поэтому T = E. Затем он работает, как и в предыдущем случае:

    List<T> list = getList();   
    T t2 = list.get(0); 
    
  3. В этом случае вы пытаетесь присвоить объект типа E переменного типа T, но E не может быть выведена и, следовательно, предполагается Object, поэтому назначение терпит неудачу:

    T t3 = getList().get(0);   
    

    Вы можете исправить это поведение путем связывания E с T вручную:

    T t3 = this.<T>getList().get(0); 
    

В случае универсального класса TestGenerics<T> у вас нет двух независимых параметров типа, поэтомув обоих методах относится к одному типу.

+0

относительно 3, почему E не может быть выведено, потому что это непрямо? это так сложно для компилятора? – oshai

+2

@ohadshai: Тип результата выводится только «Если результат метода встречается в контексте, где он будет подвергнут преобразованию присваивания» (JLS §15.12.2.8), то есть если результат сразу назначается или используется в качестве аргумента при вызове метода , – axtavt

0

Попробуйте приведения его к типу T.

public <T> List<T> getList() 
    { 
     return new ArrayList<T>(); 
    } 
    public <T> void first() 
    { 
     List<T> ret = new ArrayList<T>(); 
     List<T> list = getList(); 
     T t1 = ret.get(0); 
     T t2 = list.get(0); 
     T t3 = (T) getList().get(0); 
    } 
1

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

Для полного удовлетворения, я оставлю свой первоначальный ответ здесь. Просто не принимайте это за абсолютную истину.


Подпись java.util.List.get выглядит следующим образом:

public abstract java.lang.Object get(int arg0); 

get() возвращает объект, независимо от параметризации типа. Вот почему вы не можете назначить get(0) переменной типа T. Даже если вы можете (практически) гарантировать, что в списке всегда будет T, интерфейс просто не делает этого обещания, и компилятор не может принять ваше слово за него.

Проблема связана с типом стирания. При компиляции этого кода, он будет работать нормально:

public <T> void someMethod(java.util.List<T> list) { 
    T s = list.get(0); 
} 

компилятор знает, что имеет дело с List<T> и может использовать подпись для List<T> при компиляции. Он знает, что get() вернет T и будет полностью счастлив. Если не изменить код для этого он больше не работает:

public <T> List<T> getList() { 
    return new ArrayList<T>(); 
} 

public <T> void someMethod() { 
    T s = getList().get(0); 
} 

Причиной этого может быть то, что при составлении метода getList(), типы удаляются, и теперь он будет возвращать необщего java.util.List типа. get() на List возвращает Object, и компилятор больше не будет знать, что он был List<T> и будет жаловаться.

+0

Я добавил второй пример, так как вы можете видеть там , стирание выполняется для всего класса, а затем ошибки нет. какова причина? – oshai

+0

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

+0

Пожалуйста, прочитайте вместо него ответ на axtavts. Я считаю, что один из них правильный. –

0

Метод getList(), кажется, возвращает новый список каждый раз, когда изначально пуст. get (0) из пустого списка выдает исключение IllegalArgumentException. Это ошибка времени выполнения, поэтому она не может вызывать ошибку компиляции.

4

Вы вызываете метод generics из метода generics. Вам необходимо передать аргумент generics из первого метода методу getList.

List<T> list = this.<T>getList(); 

и

T t3 = this.<T>getList().get(0); 

Из этих двух первых компилирует и не давая аргумент дженериков, потому что компилятор может получить тип от типа списка (с левой стороны от назначения). Во втором случае это не прямое назначение, поэтому компилятор не знает тип вызова getList().

Они могут вести себя по-разному с различными компиляторами.

+0

вы можете объяснить почему? в t2 мне не пришлось это делать. – oshai

+0

@ohadshai Поскольку компилятор может автоматически найти тип при назначении результата getList() локальной переменной с правильным типом. – iny

+0

Это точно мой вопрос (и я его перефразировал). есть ли случай, когда такое автоматическое связывание неверно? – oshai

Смежные вопросы