2013-06-24 6 views
8

Посмотрите на этом простом примере Java дженериков:Список <T> не указан Список <T>?

class List<T> { 
    T head; 
    List<T> next; 
} 

class A<T> { 
    List<T> l; 

    public <T> int length() { 
     List<T> l = this.l; 
     int c = 1; 
     while (l.next != null) { 
      c++; 
      l = l.next; 
     } 
     return c; 
    } 

    public static void main(String[] args) { 
     A<Integer> a = new A<Integer>(); 
     a.l = new List<Integer>(); 
     a.l.head = 123; 
     a.l.next = new List<Integer>(); 
     a.l.next.head = 432; 
     System.out.println("list length: " + a.length()); 
    } 
} 

Он получает компиляции ошибки, утверждая, что типы несовместимы, но утверждают, что две переменные имеют одинаковый тип:

$ javac A.java && java A 
A.java:10: incompatible types 
found : List<T> 
required: List<T> 
     List<T> l = this.l; 
         ^
1 error 

Если я меняю первую строку длины() на List<T> l = (List<T>)(Object)this.l;, она работает. Зачем?

+1

Создание класса, имя которого конфликтует с таким распространенным классом JDK ('java.util.List' в этом случае), является плохой идеей. Смятение будет изобиловать. – yshavit

ответ

19

Вы объявили общий метод внутри общего класса с этой линией:

public <T> int length() { 

Это <T> отличается от вашего класса <T>. Согласно JLS Section 6.3:

Объем параметра Тип A класса (§8.1.2) является раздел объявления класса параметр типа , секция параметр типа любых суперкласса или суперинтерфейса декларации класса, и класс корпус.

Вам не нужно повторно объявлять <T> на свой метод; параметр типа класса уже находится в области видимости.

Чтобы использовать параметр универсального типа вашего класса <T>, ваш метод не объявлять другой <T> и просто использовать ваш класс <T>:

public int length() { 

Чтобы понять, почему ваш кастинг работает:

List<T> l = (List<T>)(Object)this.l; 

Вы можете лить любой объект в Object. Затем вы делаете результат List<T>. Вы всегда можете бросить его на все, что захотите; Java будет просто бросать ClassCastException во время выполнения, если это не было List во время выполнения. Но компилятор также предупреждает о том, что он использует непроверенные или небезопасные операции, потому что он не может гарантировать, что это <T> является оригиналом <T>.

Чтобы проиллюстрировать разницу между <T> с, вы можете использовать <U> в качестве параметра универсального типа для метода и получить те же результаты:

public <U> int length() { 
    List<U> l = (List<U>)(Object)this.l; 

компилируется с таким же предупреждением безопасности типа.

Если вы действительно знаете, что безопасность типа может быть гарантирована, вы можете использовать @SuppressWarnings("unchecked") для аннотации вашего метода. Но здесь я по-прежнему хотел бы удалить параметр общего типа из метода целиком и использовать параметр типа класса.

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