2012-12-08 14 views

Odpowiedz

7

W języku Java tworzenie tablicy typu ogólnego nie jest proste.

Prosta podejście nie kompiluje:

public class Container<E> { 

    E[] arr = new E[3]; // ERROR: Cannot create a generic array of E 

} 

Wymień E z Object, i wszystko jest dobrze (kosztem dodatkowej złożoności gdzie indziej w realizacji pojemnika).

Istnieją alternatywne podejścia, ale przedstawiają inny zestaw kompromisów. Dla obszernej dyskusji, patrz How to create a generic array in Java?

+0

Masz rację! Dziękuję Ci!!! – user485553

+3

Można jeszcze zrobić niezaznaczoną obsadę: "E [] arr = (E []) nowy obiekt [3];" – Saintali

+1

@Saintali: Możesz, ale dostaniesz 'ClassCastException' w momencie, w którym spróbujesz pracować z ta tablica. Spróbuj. – NPE

2

Zważywszy type erasure (czyli fakt, że parametry takie jak rodzajowych typu E W przykładzie są usuwane w rodzaju kompilacji), podejrzewam, że wygenerowany kod bajtowy byłby podobny w obu przypadkach.

Z punktu widzenia konserwacji, użycie parametru typu zamiast obiektu spowodowałoby łatwiejsze odczytanie kodu (ponieważ ograniczyłoby liczbę rzutów). Ale ponieważ API z ArrayList nigdy nie eksponuje tej "surowej" tablicy Object, to chyba nie ma to znaczenia dla nas zwykłych programistów Javy :)

4

Po pierwsze, zdaj sobie sprawę, że rzeczywisty typ środowiska wykonawczego obiektu tablicy musi być Object[]. Dzieje się tak dlatego, że tablice znają swoje typy komponentów w środowisku wykonawczym (różne typy tablic są faktycznie różnymi typami w środowisku wykonawczym), a zatem trzeba określić typ komponentu podczas tworzenia tablicy, ale obiekt ArrayList nie zna jego argumentu typu w czasie wykonywania.

Powiedział, że typ kompilacji zmiennej instancji mogłoby zostać uznane jako albo Object[] lub E[], z różnymi wadami i zaletami:

Jeżeli jest zadeklarowane jako Object[]:

private Object[] arr; 
// to create it: 
arr = new Object[3]; 
// to get an element: 
E get(int i) { return (E)arr[i]; } 

wadą tego jest to, że za każdym razem, gdy bierzesz coś z niego, musisz go rzucić pod numer E, co oznacza, że ​​zasadniczo używasz go jako pojemnika generycznego.

Jeżeli jest zadeklarowane jako E[]:

private E[] arr; 
// to create it: 
arr = (E[])new Object[3]; 
// to get an element: 
E get(int i) { return arr[i]; } 

Zaletą jest to, że nie trzeba już rzucać kiedy się rzeczy z nim - zapewnia sprawdzania typu zastosowań arr, jak generyczne pojemniki. Wadą jest to, że logicznie, obsada jest kłamstwem - wiemy, że stworzyliśmy obiekt, którego typem wykonawczym jest Object[], więc nie jest to instancja E[], chyba że E jest Object.

Jednak nie ma natychmiastowego problemu z robieniem tego, ponieważ E jest usuwany do Object wewnątrz metod instancji klasy.Jedynym sposobem, w jaki może wystąpić problem, jest to, że obiekt jest w jakiś sposób wystawiony na zewnątrz klasy (np. Zwrócony w metodzie, umieszczony w publicznym polu itp.) W wielkości, która wykorzystuje jego typ jako E[] (której nie ma) :

// This would be bad. It would cause a class cast exception at the call site 
E[] getArray() { return arr; } 

Ale ArrayList, a nawet każdy prawidłowo zaprojektowane klasa pojemnik, nigdy wystawiać szczegółów wdrażania, takie jak jego wewnętrznej tablicy na zewnątrz. Przełamałoby to między innymi abstrakcję. Tak długo, jak autor tej klasy zdaje sobie sprawę z tego, że nigdy nie wystawia tej tablicy, nie ma problemu z robieniem tego w ten sposób (z wyjątkiem może pomylić kolejną osobę, która widzi kod i jest tego nieświadomy) i jest wolna, aby wziąć zaletą zwiększonego sprawdzania typów, jakie przynosi ta metoda.

Powiązane problemy