2009-10-15 36 views
101

Antes de revisar mi estructura de datos genéricos para un índice de valores, me gustaría ver si se ha parametrizado incluso una instancia del tipo this.Java: Instanceof y genéricos

Pero Eclipse se queja cuando hago esto:

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

Este es el mensaje de error:

no puede llevar a cabo instanceof comprobar contra el parámetro de tipo E. En lugar de utilizar su borrado de objetos ya que la información de tipo genérico ser borrado en tiempo de ejecución

¿Cuál es la mejor manera de hacerlo?

Respuesta

67

El mensaje de error lo dice todo. En tiempo de ejecución, el tipo se ha ido, no hay forma de verificarlo.

Se podía cogerlo al hacer una fábrica para el objeto de la siguiente manera:

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

Y luego en la tienda constructor del objeto de ese tipo, de modo variable, de forma que su método podría tener este aspecto:

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

No creo que quiera 'Class.isAssignableFrom'. –

+0

@Tom, escribí esta última noche de memoria, y lo arreglé para pasar realmente la clase (¡duh!) Pero, por lo demás, no entiendo por qué no lo querrías (tal vez necesito más café esta mañana, estoy solo en mi primera taza). – Yishai

+0

Estoy con Tom. ¿Podrías aclarar eso? Usar isAssignableFrom() habría sido mi elección para el trabajo. Tal vez me estoy perdiendo algo? –

1

Técnicamente no debería tener que hacerlo, ese es el punto de genéricos, lo que puede hacer la comprobación de tipo compilar:

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

pero la @Override puede ser un problema si tiene una jerarquía de clases. De lo contrario, ver la respuesta de Yishai.

+0

Sí, la interfaz de lista exige que la función tome un parámetro de objeto. –

+0

estás implementando la lista? ¿Por qué no está implementando la lista ? –

+0

(consulte, por ejemplo, la declaración de ArrayList: http://java.sun.com/j2se/1.5.0/docs/api/java/util/ArrayList.html) –

1

El tipo de tiempo de ejecución del objeto es una condición relativamente arbitraria para filtrar. Sugiero mantener esa suciedad lejos de su colección. Esto se logra simplemente haciendo que su colección delegue en un filtro aprobado en una construcción.

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

(Aunque es posible que desee hacer una comprobación de tipo de instancia si está utilizando 'Comparator' o similar.) –

12

siempre y cuando su clase extiende una clase con un parámetro genérico, también se puede conseguir esto en tiempo de ejecución a través de la reflexión, y luego utilizar ese modo de comparación, es decir

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; 
    } 
} 

En el caso anterior, en tiempo de ejecución obtendrás String.class de getParameterizedClass(), y se almacena en caché para que no tengas ninguna sobrecarga de reflexión en múltiples comprobaciones. Tenga en cuenta que puede obtener los otros tipos parametrizados por índice del método ParameterizedType.getActualTypeArguments().

4

O podría detectar un intento fallido de transmitir en E , por ejemplo.

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

+1 ¡Una solución pragmática sin ingeniería para una simple comprobación! Ojalá pudiera votar esto más – higuaro

+22

Esto no funcionaría. E se borra en el tiempo de ejecución, por lo que el elenco no fallará, solo obtendrá un compilador de advertencia al respecto. – Yishai

30

Dos opciones para el tipo de tiempo de ejecución de la comprobación con los genéricos:

Opción 1 - corromper su constructor

Supongamos que usted está sustituyendo indexOf (...), y que desea compruebe el tipo solo por el rendimiento, para evitar repetir la colección completa.

Hacer un constructor sucia como esto:

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

    this.t = t; 
} 

continuación, puede utilizar isAssignableFrom para comprobar el tipo.

public int indexOf(Object o) { 

    if (
     o != null && 

     !t.isAssignableFrom(o.getClass()) 

    ) return -1; 

//... 

Cada vez que una instancia de su objeto de que tendría que repetirse:

new MyCollection<Apples>(Apples.class); 

Usted puede decidir que no vale la pena. En la implementación de ArrayList.indexOf(...), no verifican que el tipo coincida.

Opción 2 - Deje que falle

Si es necesario utilizar un método abstracto que requiere su tipo desconocido, entonces todo lo que realmente quiere es que el compilador deja de llorar sobre instanceof. Si usted tiene un método como este:

protected abstract void abstractMethod(T element); 

Usted puede utilizar de esta manera:

public int indexOf(Object o) { 

    try { 

     abstractMethod((T) o); 

    } catch (ClassCastException e) { 

//... 

Usted está echando el objeto a T (su tipo genérico), sólo para engañar al compilador. Your cast does nothing at runtime, pero igual obtendrá una ClassCastException cuando intente pasar el tipo de objeto incorrecto a su método abstracto.

NOTA 1: Si realiza moldes adicionales sin marcar en su método abstracto, sus ClassCastExceptions quedarán atrapados aquí. Eso podría ser bueno o malo, así que piénsalo bien.

NOTA 2: You get a free null check when you use instanceof. Dado que no puede usarlo, es posible que deba verificar si no es válido con sus propias manos.

7

Tuve el mismo problema y aquí está mi solución (muy humilde, @george: esta vez compilando Y trabajando ...).

Mi probem estaba dentro de una clase abstracta que implementa Observer. La actualización del método de incendios observables (...) con la clase de objeto que puede ser cualquier tipo de objeto.

sólo quiero al controlador de objetos de tipo T

La solución es pasar la clase al constructor con el fin de poder comparar los tipos en tiempo de ejecución.

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); 

} 

Para la ejecución sólo tenemos que pasar la clase al constructor

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

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

Viejo poste, sino una manera simple de hacer la comprobación instanceOf genérico.

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

No está claro qué estás contribuyendo aquí. – Andrew