2013-02-14 26 views
7

Dlaczego kolekcje niezwiązane z klasą szablonów upuszczają swój typ? Oto przykład: (Niestety, nie będzie kompilować z powodu błędu jestem zagubiony.)Ogólna liczba niepowiązanych kolekcji

package test; 

import java.util.ArrayList; 
import java.util.List; 

public class TemplateTest { 

    public static class A { } 

    public static class B<T extends Comparable> { 
     List<A> aList = new ArrayList<A>(); 

     public List<A> getAList() { 
      return aList; 
     } 

     public int compare(T t, T t1) { 
      return t.compareTo(t1); 
     } 
    } 

    public static void main(String[] args) { 
     B b = new B(); 
     for (A a : b.getAList()) { //THIS DOES NOT WORK 

     } 
     List<A> aList = b.getAList(); //THIS WORKS 
     for (A a : aList) { 

     } 
    } 
} 

Ten kod generuje błąd przy kompilacji:

test/TemplateTest.java:24: incompatible types 
    found : java.lang.Object 
    required: test.TemplateTest.A 
     for (A a : b.getAList()) { 

Gdybym określić szablon z B, takich jak B<String>, lub jeśli całkowicie usunę szablon z B, wszystko jest w porządku.

Co się dzieje?

EDIT: ludzie wskazał, nie było potrzeby, aby B generic więc dodałem do B

+1

Przepraszam za nonsens przed ... Wydaje się to do być związane z błędem. Sława! –

+1

Nie jest to ściśle związane z pytaniem, ale * nie * określenie parametru typu w Javie jest ogólnie uważane za błąd programowania. Użycie typu surowego kompiluje się tylko jako ustępstwo dla wstecznej zgodności. – Affe

+0

Również nazywam błąd kompilatora. Wygląda na to, że rodzaj wyrażenia po prawej stronie, zastosowanego w ulepszonej pętli for, jest określany w sposób szczególny (co nie jest zaskakujące, ponieważ wymaga specjalnych tablic), a to się psuje, gdy chodzi o typ surowy. (Tzn. Widząc gdzieś surowego typu, zakłada, że ​​całe wyrażenie ma usunięte typy). – millimoose

Odpowiedz

8

Tak, wiadomo, zachowanie, że w przypadku korzystania z surowego typu, a następnie wszystkie parametry typu na klasie zostaną utracone, a nie tylko parametr, którego nie udało się zadeklarować.

Problem jest częściowo tutaj:

Gdybym określić szablon B jak B<String>, lub jeśli usunąć szablon od B całkowicie, to wszystko jest w porządku.

To nie jest opcja, nie musisz wybierać, jeśli chcesz określić parametr typu, czy nie. Jedynym powodem, dla którego kompiluje się w ogóle bez określonego parametru, jest zgodność wsteczna. Zapisanie nowego kodu z brakującymi parametrami typu jest błędem programowania.

List<A> list = b.getList() nie interpretuje z powodzeniem typu, tylko skutecznie przykleja się do arbitralnej obsady i wierzy, że zadanie jest poprawne. Jeśli spojrzysz na ostrzeżenia kompilatora, generujesz ostrzeżenie o niebezpiecznej konwersji.

for(A a : b.getList()) {} uaktualnienia ostrzegające przed błędem, ponieważ wstawione odlewanie znajdowało się wewnątrz kodu generowanego przez kompilator, więc odmawia automatycznego generowania niebezpiecznego kodu, a nie tylko ostrzega.

Od specyfikacji języka Java:

Wykorzystanie surowców typów jest dozwolony tylko jako ustępstwo kompatybilności kodu starszego typu. Używanie typów surowych w kodzie napisanym po wprowadzeniu generyczności do języka programowania Java jest mocno odradzane. Możliwe, że przyszłe wersje języka programowania Java uniemożliwią stosowanie typów surowych.

Konkluzja naprawdę jest to, że jedyną istotną rzeczą rodzajowych java akcji z szablonami C++ jest <> składni :)

Więcej szczegółów: What is a raw type and why shouldn't we use it?

+0

Ale Sun akceptuje błąd, o którym wspomniałem w http://stackoverflow.com/a/14882182/688653. Czy to błąd, czy właściwe zachowanie? –

+0

Dobra, ale to nie wyjaśnia, dlaczego typ wyrażenia jest poprawnie określony * poza * kontekstem ulepszonej pętli for. – millimoose

+0

@millimoose Nie sądzę, że jest określona poprawnie na zewnątrz, ale dostaję ostrzeżenie zamiast błędu. np. 'List list = notDeclared.getAList();' daje mi ostrzeżenie "niezaznaczone konwersja". – sharakan

1

Po pierwsze, w Javie, to rodzajowych, a nie jak to jest Szablony w C++.

Deklarujesz ogólny parametr typu T w swojej klasie B, ale go nie używasz. Powinieneś używać T zamiast A w całej swojej definicji klasy B.

public static class B<T> { 
    List<T> aList = new ArrayList<T>(); 

    public List<T> getAList() { 
     return aList; 
    } 
} 

Następnie należy użyć parametru typu kiedy zadeklarować instancji klasy B.

B<A> b = new B<A>(); 

Ale jeśli wiesz, że zmienna aList zawsze trzymać przedmiotów typu A jako nazwa metody getAList sugeruje, wtedy nie byłoby żadnego powodu, aby klasa B rodzajowy.

+0

Mogę użyć T gdzie indziej, ale nawet jeśli to zrobię, wkręca A, jeśli nie określę T. Dlaczego tak jest? – naugler

+0

Określenie ogólnego parametru typu dla klasy B oznacza, że ​​jego metoda "getAList" zwróci listę określonego typu "T", ale nie obchodzi nas, jaki to jest typ. Typ "T" nie jest określony, dopóki nie zostanie utworzona instancja. Kiedy tworzysz swoją instancję "b" typu "B", określasz parametr typu "A", aby po wywołaniu "getAList" odzyskać Listę . – rgettman

+0

Nie, chcę, aby lista była typu A. Typ T jest używany w innym miejscu. Dlaczego moja lista obiektów A upuszcza ten typ? (Dodałem do mojego przykładu, aby pokazać możliwe użycie T, przepraszam, że jest prymitywne) – naugler

1

Jest to bardzo podobne do tego błędu:

6760983 : Unused type parameter triggering error in unrelated method

które jest zgłaszane tutaj:

Unused Generic causing problem

+0

Ale kod OP nie jest podklasowany, więc nie ma żadnych nadpisań ani kolizji nazw, jak sugeruje błąd. – splungebob

+0

Prawda, ale w tym błędzie dodatkowy parametr pomieszał kompilator, tak że sprawdzanie typów na dwóch (pozornie) pasujących typach nie powiodło się, tak jak tutaj. Może są spokrewnieni? Kolejna myśl: może inna implementacja Java może pomóc tutaj? –

+0

@ZiyaoWei Możesz wypróbować kompilator Eclipse zamiast Sun. – millimoose