2012-02-05 16 views
9

Proszę wyjaśnić ten kod rodzajowy błąd czasu kompilacji wieloznaczny:Tworzenie nowego obiektu ogólnego zamiennika

//no compile time error. 
List<? extends Number> x = new ArrayList<>(); 

//compile time error. 
List<? extends Number> x = new ArrayList<? extends Number>(); 
+0

Która wersja Java? – Bill

+2

Pierwszy przykład działa na Java 7 oczywiście, a nie na Java 6, ponieważ opcjonalne diamenty nie były dozwolone w 6. drugim przykładzie nie udać się w obu –

+1

Pierwszy zdecydowanie nie działałby na 6; bez operatora diamentowego. Co do reszty; możesz sprawdzić ten zasób: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html – Bill

Odpowiedz

22

Niepoprawna składnia do utworzenia typu ogólnego za pomocą symboli wieloznacznych. Typ List<? extends Number> oznacza pewien typ, który jest lub rozciąga się na Number. Aby utworzyć wystąpienie tego typu nie ma sensu, ponieważ z konkretyzacji tworzysz coś konkretnego:

new ArrayList<? extends Number>();//compiler:"Wait, what am I creating exactly?" 

typy generyczne z symboli wieloznacznych sens tylko dla zmiennych i parametrów metody, ponieważ pozwala to na większą swobodę w co można je przypisać/przekazać.

//compiler:"Okay, so passing in a List<Integer> or a List<Double> are both fine" 
public void eatSomeNumbers(List<? extends Number> numbers) { 
    for (Number number : numbers) { 
     System.out.println("om nom " + number + " nom"); 
    } 
} 

Należy pamiętać o ograniczeniach związanych z używaniem symboli wieloznacznych.

List<? extends Number> numList = ... 
numList.add(new Integer(3));//compiler:"Nope, cause that might be a List<Double>" 

Jak na pierwszy przykład diament to nowa funkcja w Java 7, który umożliwia kompilator wywnioskować rodzaju nowej instancji rodzajowe, w zależności od typu zmiennej to przypisany. W tym przypadku:

List<? extends Number> x = new ArrayList<>(); 

Kompilator jest najprawdopodobniej wywodząc new ArrayList<Number>() tutaj, ale co wywnioskować nie ma znaczenia, tak długo, jak jest to ważne zadanie dla danej zmiennej. To był powód, dla którego wprowadzono operatora diamentu - że określenie ogólnego rodzaju nowego obiektu było zbędne, tak długo jak typ uczyniłby go ważnym przydziałem/argumentem.

To rozumowanie ma sens tylko wtedy, gdy pamiętasz, że generics w Javie są czysto kompilacją funkcji językowych, ze względu na type erasure i nie mają znaczenia w czasie wykonywania. Symbole wieloznaczne istnieją tylko z powodu tego ograniczenia. Natomiast w języku C# informacje ogólne pojawiają się w środowisku wykonawczym, a ogólne symbole wieloznaczne nie istnieją w tym języku.

+0

+1: Bardzo szczegółowe wyjaśnienie –

+0

Czy istnieje sposób na obejście tego ograniczenia podczas tworzenia instancji anonimowych? – shmosel

+0

@shmosel Nie sądzę, ale nie jestem pewien, jaka byłaby korzyść z np. 'nowy Foo () {}' over 'nowy Foo () {}'. Czy możesz wyjaśnić więcej o swoim przypadku użycia? –

1

Korzystając

List<? extends Number> x = new ArrayList<Number>(); 

zamiast.