2013-06-19 13 views
12

Nie rozumiem błędu kompilatora wynikającego z następującego kodu. Definiuję ogólny interfejs, zobacz Zadanie, dwie metody: U doSomething(String value) i List<Integer> getIDs(). Metoda doSomething() używa generycznego typu jako typu zwracanej wartości, ale nie wydaje się powodować problemów. Metoda getIDs() zwraca listę, która nie jest związana z typem zadania, ale powoduje problemy podczas używania dla instrukcji..acach do iterowania nad wartością zwracaną. Wystąpił następujący błąd kompilatora.Błąd kompilatora w ogólnym interfejsie języka Java z listą <> metoda

error: incompatible types 
    for (Integer value : task.getIDs()){ 
required: Integer 
found: Object 

Wydaje się, że wymazanie typ na interfejsie powoduje kompilator zapomnieć deklarowany typ na drugą metodą, która nie jest związana z typu rodzajowego. Innymi słowy, dlaczego typowy interfejs ma wpływ na to, jak kompilator rozumie wartość zwracaną w metodzie getIDs(), a konkretnie w kontekście instrukcji for..each?

Najwyraźniej, jeśli mam odniesienie do listy poza ... dla .. nie ma problemu, ale nie bezpośrednio.

public class InterfaceTest { 
    public static void main(String[] args) { 
     Task task = new MyTask(); 
     // no complaints about the type here  
     List<Integer> values = task.getIDs(); 

     // getting a compiler error for this line 
     for (Integer value : task.getIDs()){ 

     } 
    } 
} 


interface Task<U>{ 
    U doSomething(String value); 
    List<Integer> getIDs(); 
} 

Realizacja interfejsu nie jest konieczne wykazanie punkt, ale nie chciał opuścić odniesienie Task task = null; i mieć odpowiedź mi mówi, że jest problem.

class MyTask implements Task<Boolean>{ 

    @Override 
    public Boolean doSomething(String value) { 
     System.out.println(value); 
     return false; 
    } 

    @Override 
    public List<Integer> getIDs() { 
     return Arrays.asList(1, 2, 3, 4); 
    } 
} 
+0

Co robi to z 'return new int [] {1,2,3,4}' lub 'return Arrays.asList (new Integer (1), new Integer (2), ...);' ? (zauważ, nie hipotetycznie, spróbuj, dodaj wynik do pytania, aby pokazać, że boks/odpakowanie wyraźnie robi coś dziwnego) –

+0

To jest bardzo dobre pytanie; odpowiedź jest prawdopodobnie ukryta głęboko w wnętrznościach JLS. – arshajii

+1

@arshajii: Czy zmieniłeś 'U doSomething (wartość ciągu)' z 'U doSomething (wartość U)'? Wydaje mi się, że opublikowany kod faktycznie nie spełnia tego, co robi OP. –

Odpowiedz

7

Co się dzieje, kiedy użycie użyć klasy (lub Interface) z ogólnym parametrem <T> ale odnoszą się do instancji bez <T> (tj raw) kompilator usuwa wszystkie ogólne informacje o typie z klasy. Jest to prawdopodobnie spowodowane zgodnością z kodem źródłowym sprzed 1,5, gdy nie można w ogóle używać ogólnych informacji o typie.

Rozważ sytuację, w której piszesz kod i kompilujesz na kompilatorze Java 1.4. Chcesz użyć biblioteki, która korzysta z generycznych. Gdy odwołujesz się do typu z tej biblioteki, która ma ogólne parametry jako typ surowy, kompilator wymusza użycie żadnych ogólnych parametrów.

Edycja:

The JLS-4.8-210 nawiązuje do tego, kiedy wspomina (źródło: zhong-j-yu)

Typ konstruktor (§8.8), sposób wystąpienie (§8.4, §9.4) lub pole niestatyczne (§ 8.3) M surowego typu C, który nie jest dziedziczony z jego nadklas lub superinterfejsów, jest typem surowym, który odpowiada usunięciu jego typu w ogólnej deklaracji odpowiadającej C.

To wciąż wydaje się być grzechotką, ale jest prawdopodobne z jakiegoś powodu.

+0

http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.8-210 Typ ... metody instancji ... typu surowego ... jest typem surowym, który ... – ZhongYu

+0

Wierzę, że znalazłem odpowiednią specyfikację JLS., zobacz moją odpowiedź. – Kevin

3

Błąd wydaje się leżeć tutaj:

Task task = new MyTask(); 

Zapomniałeś dodać rodzajowych po Task. To powinno działać, jeśli go zmienić na jedną z nich.

Task<Boolean> task = new MyTask(); 
Task<?> task = new MyTask(); 
+1

Pytanie brzmi, dlaczego nie działa tak jak jest. – arshajii

+0

@arshajii nie mieli zasobu/czasu, więc bardzo surowo potraktowali surowe typy. – ZhongYu

3

Jeśli jestem interpretacji Java Language Specification (Type Erasure. §4.6) prawidłowo, to jest "haczyka" w języku:

Typ wymazywania odwzorowuje również podpis (§8.4.2) o konstruktorze lub metoda do podpisu, który nie ma sparametryzowanych typów lub zmiennych typu. Wymazanie sygnatury konstruktora lub metody s jest sygnaturą składającą się z tej samej nazwy co s oraz wymazań wszystkich formalnych typów parametrów podanych w s.

wierzę, że ten stwierdza, że ​​jeśli zadeklarować typ (Task), który jest zadeklarowany z parametru rodzajowego (Task<U>) bez wspomnianego generyczny parametr, wszystkie jego funkcje również tracą rodzajowe typy, czy są one związane lub nie . Dlatego Twój task.getIDs() jest interpretowany przez kompilator jako zwracający zwykły List, a nie List<Integer>. Iterator do tego, oczywiście, produkuje Objects, a nie Integers, powodując błąd kompilatora.

Powodem tego jest prawdopodobnie kompatybilność wsteczna z kodem wyprodukowanym przed wersją Java 1.5, gdy wprowadzono generyczne.