pila - Java: instancia de y genéricos
objeto t en java (8)
Dos opciones para verificar el tipo de tiempo de ejecución con genéricos:
Opción 1: corrompe a su constructor
Supongamos que está anulando indexOf (...), y desea verificar el tipo solo por el rendimiento, para ahorrarse iterando toda la colección.
Haz un constructor asqueroso como este:
public MyCollection<T>(Class<T> t) {
this.t = t;
}
Luego puede usar isAssignableFrom para verificar el tipo.
public int indexOf(Object o) {
if (
o != null &&
!t.isAssignableFrom(o.getClass())
) return -1;
//...
Cada vez que crea una instancia de su objeto, debería repetirlo:
new MyCollection<Apples>(Apples.class);
Puede decidir que no vale la pena. En la implementación de ArrayList.indexOf(...) , no verifican que el tipo coincida.
Opción 2: dejar que falle
Si necesita usar un método abstracto que requiera su tipo desconocido, entonces todo lo que realmente desea es que el compilador deje de llorar sobre instanceof . Si tienes un método como este:
protected abstract void abstractMethod(T element);
Puedes usarlo así:
public int indexOf(Object o) {
try {
abstractMethod((T) o);
} catch (ClassCastException e) {
//...
Está transfiriendo el objeto a T (su tipo genérico), solo para engañar al compilador. Tu elenco no hace nada en el tiempo de ejecución , pero igual obtendrás una ClassCastException cuando trates de pasar el tipo de objeto incorrecto a tu método abstracto.
NOTA 1: Si está realizando conversiones adicionales no verificadas en su método abstracto, sus ClassCastExceptions quedarán atrapadas aquí. Eso podría ser bueno o malo, así que piénsalo bien.
NOTA 2: Obtienes una verificación nula gratuita cuando usas instanceof . Dado que no puede usarlo, es posible que deba verificar la nulidad con sus propias manos.
Antes de revisar mi estructura de datos genéricos para el índice de un valor, me gustaría ver si es incluso una instancia del tipo al que se ha parametrizado.
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 se puede realizar una instancia de verificación contra el parámetro de tipo E. Utilice en su lugar su Objeto de borrado ya que la información de tipo genérico se borrará en el tiempo de ejecución
¿Cuál es la mejor manera de hacerlo?
El mensaje de error lo dice todo. En tiempo de ejecución, el tipo se ha ido, no hay forma de verificarlo.
Podrías atraparlo haciendo una fábrica para tu objeto como esta:
public static <T> MyObject<T> createMyObject(Class<T> type) {
return new MyObject<T>(type);
}
Y luego en el almacén de constructores del objeto que escriba, tan variable para que su método se vea así:
if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass()))
{
return -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;
}
}}
);
O podrías atrapar un intento fallido de lanzar en E por ej.
public int indexOf(Object arg0){
try{
E test=(E)arg0;
return doStuff(test);
}catch(ClassCastException e){
return -1;
}
}
Publicación anterior, pero una forma simple de hacer una instancia genérica de verificación.
public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) {
return clazz.isInstance(targetClass);
}
Siempre que su clase amplíe una clase con un parámetro genérico, también puede obtener esto en tiempo de ejecución a través de la reflexión, y luego usar eso para la 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 ().
Técnicamente no debería tener que hacerlo, ese es el punto de los genéricos, por lo que puede hacer una comprobación de tipo compilación:
public int indexOf(E arg0) {
...
}
pero la @Override puede ser un problema si tiene una jerarquía de clase. De lo contrario, ver la respuesta de Yishai.
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. El Observable dispara la actualización del método (...) con la clase Object que puede ser cualquier clase de Object.
Solo quiero manejar objetos de tipo T
La solución es pasar la clase al constructor para poder comparar 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 implementación solo tenemos que pasar la clase al constructor
public class OneImpl extends AbstractOne<Rule> {
public OneImpl() {
super(Rule.class);
}
@Override
public void foo(Rule t){
}
}