2012-10-14 16 views
7

Mam kontener (lista) niektórych elementów typu T i chcę go filtrować. Zawiera więc tylko elementy określonego podtypu U. Czy byłoby możliwe ustawienie "dynamicznego" typu zwrotu?Typ dynamicznego zwrotu Java?

przykład:

class SomeContainer<T> extends ArrayList<T>{ 

    public SomeContainer<T> subset(Class c){ 
     SomeContainer<...here the type of c > output = new SomeContainer<.. also ..>(); 

     //filter own elements and only add c-objects in the new list 

     return output; 
    } 
} 

Obecnie zwraca listę generycznego typu T i nie stanowi Klasy C-type (podtyp T). Dlatego czasami pojawia się następujący kompilator Wskazówka:

Note: SomeContainer.java uses unchecked or unsafe operations. 
Note: Recompile with -Xlint:unchecked for details. 

Bo chcę filtrować listę po obiektów o metodach podtypu i spustowych podtypu specyficzne musiałbym konkretną listę podtypu.

+2

Ostrzeżenie dotyczy tego, że używasz 'Class' zamiast' Class '. I nie jestem pewien, czy możesz używać typów dynamicznych w parametrach szablonu. – vainolo

+0

* Zatem zawiera tylko elementy określonego podtypu U * Dlaczego nie zadeklarować z tym konkretnym podtypem U? – m0skit0

Odpowiedz

9

java.lang.Class to typ rodzajowy parametryzowane na siebie, dzięki czemu można używać jej parametr typu, na przykład:

public <U extends T> SomeContainer<U> subset(Class<U> c){ 
    SomeContainer<U> output = new SomeContainer<U>(); 
    for (T val : this) { 
     if (c.isInstance(val)) { 
      output.add(c.cast(val)); 
     } 
    } 
    return output; 
} 
+3

Lub lepiej, 'output.add (c.cast (val))', aby uniknąć niezaznaczonego ostrzeżenia obsady. –

+0

@IanRoberts Absolutnie! Dzięki za wspaniałą sugestię! – dasblinkenlight

1

rodzajowych to tylko artefakt kompilacji, dlatego Twój system nie może pracować. Kompilator nie może przewidzieć, jaka klasa będzie potrzebna przy każdym wykonaniu linii kodu, która wywołuje tę funkcję. Nie można tego typu bezpieczeństwa typu , chyba że masz bardzo ograniczony, i całkiem bezużyteczny przypadek, w którym używasz tylko literówek klasowych do wywołania swojej funkcji. Byłoby to jednak prawie na pewno niemożliwe do spełnienia, by być, jak twierdzisz, dynamicznym.

0

Pierwszy punkt: możesz usunąć niezaznaczone ostrzeżenie o operacji, zastępując argument Class c wartością Class<T> c. To może być wszystko, czego potrzebujesz ... w takim przypadku ... = :-)

Drugi punkt: Zwykle kod wywołujący SomeContainer.subset() będzie znał podczas kompilacji typ U (z kontekstu logicznego) . To musi być sprawa dla ciebie, w przeciwnym razie nie będzie w stanie przekazać Class c argument w

Spróbuj:.

class SomeContainer<T> extends ArrayList<T>{ 

    public <U extends T> SomeContainer subset(Class<U> c){ 
     SomeContainer<U> output = new SomeContainer<U>(); 
     // put filtered elements into output 

     return output;  
    } 
} 

Zobacz co zrobiłem tam?
Wprowadzono drugi parametr w wywołaniu metody: U extends T. Użyto tego również w argumencie z Class<U> c.
Rozmówca będzie powoływać się jak (gdzie X jest wybrany podklasę T):

SomeContainer<X> mySubset = mySomeContainer.subset(X.class);  // type inference 
SomeContainer<X> mySubset = mySomeContainer.<X>subset(X.class); // type arg specified 


Jeśli potrzebujesz czegoś bardziej dynamiczny niż ten, symbole wieloznaczne mogą pomóc - pozwalającą na „rodziny” parametised typy być przekazywane w & out:

public SomeContainer<? extends X> subset(Class<? extends X> c){ 

jest to „plastikowy” interfejs funkcji: można powrócić SomeContainer<T> lub SomeContainer<X> dla każdego X, które jest podklasy T. następuje również działa:

public SomeContainer<? super Z> subset(Class<? extends X> c){ 

Jednakże w innym Poster powiedział generyczni konstrukt czasie kompilacji, są wymienione podczas kompilacji z wygenerowanego kodu nierodzajowego. Oznacza to, że nie można dynamicznie decydować o typie używanym do utworzenia typu ogólnego za pomocą pojedynczej linii kodu.Ale możesz oszukać trochę: Jeśli masz ograniczoną liczbę podklas T, powiedzmy X, Y i Z, gdzie Z rozszerza Y, a Y rozszerza Z, wtedy możesz użyć staroświeckiego, hacky "jeśli instrukcja". Spróbuj:

klasa SomeContainer rozciąga ArrayList {

public SomeContainer<? extends X> subset(Class<? extends X> c){ 
    SomeContainer<? extends X> output = null; 

    // would like to use: "if (c instance of Class<Z>)" 
    // but instanceof does not allow generic type arguments 
    if (c.getName().equals(Z.class.getName())) { 
     SomeContainer<Z> outputZ = new SomeContainer<Z>(); 
     // put filtered elements into outputZ 
     output = outputZ; 
    } else if (c.getName().equals(Y.class.getName())) { 
     SomeContainer<Y> outputY = new SomeContainer<Y>(); 
     // put filtered elements into outputZ 
     output = outputY; 
    } else if (c.getName().equals(X.class.getName())) { 
     SomeContainer<X> outputX = new SomeContainer<X>(); 
     // put filtered elements into outputZ 
     output = outputX; 
    } 
    return output;  
} 

}

proste! (lub nie) = :-)