2009-10-15 20 views
101

Zanim przejrzę moją ogólną strukturę danych dla indeksu wartości, chciałbym sprawdzić, czy jest to nawet instancja typu this została sparametryzowana.Java: Instanceof i Generics

Ale Eclipse narzeka, kiedy to zrobić:

@Override 
public int indexOf(Object arg0) { 
    if (!(arg0 instanceof E)) { 
     return -1; 
    } 

To jest komunikat o błędzie:

Nie można wykonać instanceof sprawdzić przed parametrem typu E. użytkowania zamiast jego usuwanie obiektu od informacji ogólnych typu będzie być wymazane w czasie wykonywania

Jaki jest lepszy sposób to zrobić?

Odpowiedz

67

Komunikat o błędzie mówi wszystko. W czasie wykonywania typ nie istnieje, nie ma sposobu, aby to sprawdzić.

Można by go złapać poprzez fabrykę dla obiektu tak:

public static <T> MyObject<T> createMyObject(Class<T> type) { 
    return new MyObject<T>(type); 
} 

A potem w konstruktora sklepie obiektu tego typu, więc zmienna tak, że metoda może wyglądać następująco:

 if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass())) 
     { 
      return -1; 
     } 
+2

Nie sądzę, że chcesz "Class.isAssignableFrom". –

+0

@Tom, napisałem tę ostatnią noc z pamięci, i naprawiłem ją, aby faktycznie przejść zajęcia (duh!), Ale poza tym, nie rozumiem, dlaczego nie chciałbyś tego (może potrzebuję więcej kawy dziś rano, ja tylko na mojej pierwszej filiżance). – Yishai

+0

Jestem z Tomem. Czy możesz to wyjaśnić? Używanie metody isAssignableFrom() byłoby moim wyborem do tej pracy. Może coś mi brakuje? –

1

technicznie nie powinno się, że jest sens generycznych, więc można zrobić typu kompilacji sprawdzanie:

public int indexOf(E arg0) { 
    ... 
} 

, ale wtedy @Override może stanowić problem, jeśli masz hierarchię klas. W przeciwnym razie zobacz odpowiedź Yishai.

+0

Tak, interfejs listy wymaga, aby funkcja przyjęła parametr obiektu. –

+0

implementujesz listę? Dlaczego nie wdrażasz listy ? –

+0

(patrz na przykład deklaracja ArrayList: http://java.sun.com/j2se/1.5.0/docs/api/java/util/ArrayList.html) –

1

Typ środowiska wykonawczego obiektu jest względnie arbitralnym warunkiem do filtrowania. Sugeruję, aby zachować takie niewygody z dala od swojej kolekcji. Osiąga się to po prostu poprzez przekazanie swojej kolekcji do filtra przekazanego w konstrukcji.

public interface FilterObject { 
    boolean isAllowed(Object obj); 
} 

public class FilterOptimizedList<E> implements List<E> { 
    private final FilterObject filter; 
    ... 
    public FilterOptimizedList(FilterObject filter) { 
     if (filter == null) { 
      throw NullPointerException(); 
     } 
     this.filter = filter; 
    } 
    ... 
    public int indexOf(Object obj) { 
     if (!filter.isAllows(obj)) { 
       return -1; 
     } 
     ... 
    } 
    ... 
} 

    final List<String> longStrs = new FilterOptimizedList<String>(
     new FilterObject() { public boolean isAllowed(Object obj) { 
      if (obj == null) { 
       return true; 
      } else if (obj instanceof String) { 
       String str = (String)str; 
       return str.length() > = 4; 
      } else { 
       return false; 
      } 
     }} 
    ); 
+0

(Chociaż możesz chcieć wykonać typ instancji, sprawdź, czy używasz 'Komparatora' lub podobnego.) –

12

Pod klasa rozszerza klasę z parametru rodzajowego, można również uzyskać to przy starcie poprzez odbicie, a następnie użyć dla porównania, tj

class YourClass extends SomeOtherClass<String> 
{ 

    private Class<?> clazz; 

    public Class<?> getParameterizedClass() 
    { 
     if(clazz == null) 
     { 
     ParameterizedType pt = (ParameterizedType)this.getClass().getGenericSuperclass(); 
      clazz = (Class<?>)pt.getActualTypeArguments()[0]; 
     } 
     return clazz; 
    } 
} 

W przypadku powyżej, w czasie wykonywania otrzymasz String.class z getParameterizedClass(), i buforuje, dzięki czemu nie otrzymasz narzutów na wiele czeków. Zauważ, że możesz uzyskać inne sparametryzowane typy według indeksu z metody ParameterizedType.getActualTypeArguments().

4

Możesz też złapać nieudaną próbę wrzucenia do E np.

public int indexOf(Object arg0){ 
    try{ 
    E test=(E)arg0; 
    return doStuff(test); 
    }catch(ClassCastException e){ 
    return -1; 
    } 
} 
+1

+1 Pragmatyczne, nie over over zaprojektowane rozwiązanie dla prostego wyboru! Chciałbym móc głosować na to więcej – higuaro

+22

To by nie działało. E jest usuwany w czasie wykonywania, więc rzutowanie nie zawiedzie, otrzymasz ostrzeżenie kompilatora. – Yishai

30

dwie opcje do sprawdzania typu wykonawczego z rodzajowych:

Wariant 1 - Corrupt konstruktora

Załóżmy, że są nadrzędne indexOf (...) i chcesz sprawdź typ tylko dla osiągnięcia wydajności, aby zapisać siebie w iteracji całej kolekcji.

Zrób brudny konstruktor tak:

public MyCollection<T>(Class<T> t) { 

    this.t = t; 
} 

Następnie można użyć isAssignableFrom do sprawdzenia typu.

public int indexOf(Object o) { 

    if (
     o != null && 

     !t.isAssignableFrom(o.getClass()) 

    ) return -1; 

//... 

każdorazowym wystąpienia swój obiekt trzeba by powtarzać sobie:

new MyCollection<Apples>(Apples.class); 

Możecie zdecydować, że nie warto. W implementacji ArrayList.indexOf(...) nie sprawdzają, czy typ jest zgodny.

Wariant 2 - Niech to nie

Jeśli trzeba użyć abstrakcyjną metodę, która wymaga nieznanego typu, to wszystko, czego naprawdę chcę to kompilator przestać płakać o instanceof. Jeśli masz metody takie jak to:

protected abstract void abstractMethod(T element); 

Można go używać tak:

public int indexOf(Object o) { 

    try { 

     abstractMethod((T) o); 

    } catch (ClassCastException e) { 

//... 

Jesteś odlewania obiekt do T (typu generycznego), po prostu oszukać kompilatora. Your cast does nothing at runtime, ale nadal będzie występować wyjątek ClassCastException podczas próby przekazania niewłaściwego typu obiektu do abstrakcyjnej metody.

UWAGA 1: Jeśli wykonujesz dodatkowe niezaznaczone rzuty w swojej abstrakcyjnej metodzie, twoje Wyrażenia Klasyfikacji Klasowej zostaną tu złapane. To może być dobre lub złe, więc przemyśl to.

UWAGA 2: You get a free null check when you use instanceof. Ponieważ nie możesz go użyć, możesz sprawdzić zerową wartość gołymi rękami.

7

Miałem ten sam problem i oto moje rozwiązanie (bardzo skromne, @george: tym razem kompilacja I działa ...).

Mój probem był wewnątrz abstrakcyjnej klasy, która implementuje Obserwatora. Aktualizacja metody obserwowanych pożarów (...) za pomocą klasy Object, która może być dowolnym obiektem.

Chcę tylko handler obiektów typu T

Rozwiązaniem jest przechodzić z klasy do konstruktora, aby móc porównać typy przy starcie.

public abstract class AbstractOne<T> implements Observer { 

    private Class<T> tClass; 
    public AbstractOne(Class<T> clazz) { 
    tClass = clazz; 
    } 

    @Override 
    public void update(Observable o, Object arg) { 
    if (tClass.isInstance(arg)) { 
     // Here I am, arg has the type T 
     foo((T) arg); 
    } 
    } 

    public abstract foo(T t); 

} 

Dla realizacji po prostu musimy przekazać do konstruktora klasy

public class OneImpl extends AbstractOne<Rule> { 
    public OneImpl() { 
    super(Rule.class); 
    } 

    @Override 
    public void foo(Rule t){ 
    } 
} 
9

Stary post, ale w prosty sposób zrobić rodzajowe sprawdzania instancją.

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) { 
    return clazz.isInstance(targetClass); 
} 
+3

Nie jest jasne, co tak naprawdę przyczyniasz się tutaj. – Andrew