2010-10-15 19 views
9

Este es mi código: El ExecutorImp extiende AbstractExecutor que extrae las mismas lógicas de ejecución de sus ejecutores (ExecutorImp es un caso), al llamar al método execute() de ExecutorImp, llamará al método en su supertipo, pero el supertipo (el AbstractExcutor) debe conocer otra clase de unión al implementador (en el ejemplo, es la clase de usuario):¿Cómo obtener el tipo genérico en tiempo de ejecución?

import java.lang.reflect.InvocationTargetException; 
import java.util.ArrayList; 

abstract class AbstractExecutor<E> { 
    public void execute() throws Exception { 
     ArrayList<E> list = new ArrayList<E>(); 
     // here I want to get the real type of 'E' 
     Class cl = this.getClass().getTypeParameters()[0].getGenericDeclaration().getClass(); 
     Object o = cl.getConstructor(String.class).newInstance("Gate"); 
     list.add((E) o); 
     System.out.println(format(list)); 
    } 
    public abstract String format(ArrayList<E> list); 
    public abstract String getType(); 
} 

public class ExectorImp<E> extends AbstractExecutor<User> { 
    @Override 
    public String getType() { 
     return "user"; 
    } 
    @Override 
    public String format(ArrayList<User> list) { 
     StringBuffer sb = new StringBuffer(); 
     for (User u : list) { 
      sb.append(u.toString() + " "); 
     } 
     return sb.toString(); 
    } 
    public static void main(String[] args) throws Exception { 
     new ExectorImp().execute(); 
    } 
} 
class User { 
    String name; 
    public User(String name) { 
     this.name = name; 
    } 
} 

así, cuál es el problema con mis códigos?

+0

Quizás esta respuesta puede ayudar con su problema. http: // stackoverflow.com/questions/6624113/get-type-name-for-generic-parameter-of-generic-class/19454610 # 19454610 –

Respuesta

12

Aquí hay algo de confusión. Debido al tipo de borrado no se puede obtener el tipo de información de la ejecución de tipo parametrizado como:

Class<E> cls = E.getClass(); // Error. 
E e = new E(); // Error. 

Sin embargo, se puede obtener información de tiempo de compilación tipo parametrizado de la clase, el campo y declaración de método por ParameterizedType#getActualTypeArguments().

abstract class AbstractExecutor<E> { 

    public void execute() throws Exception { 
     List<E> list = new ArrayList<E>(); 
     Class<E> cls = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 
     E e = cls.getConstructor(String.class).newInstance("Gate"); 
     list.add(e); 
     System.out.println(format(list)); 
    } 

    // ... 
} 

actualización: en cuanto a si esto es recomendable o no, aunque esto va a funcionar, esto es sensible a los problemas de tiempo de ejecución siempre que se produzcan cambios menores en la declaración de clase. Usted como desarrollador debe documentarlo adecuadamente. Como una alternativa completamente diferente, puedes utilizar el polimorfismo.

abstract class AbstractExecutor<E> { 

    public void execute() throws Exception { 
     List<E> list = new ArrayList<E>(); 
     E e = create("Gate"); 
     list.add(e); 
     System.out.println(format(list)); 
    } 

    public abstract E create(String name); 

    // ... 
} 

e implementar UserExecutor en consecuencia.

class UserExecutor extends AbstractExecutor<User> { 

    @Override 
    public User create(String name) { 
     return new User(name); 
    } 

    // ... 
} 
+0

Muchas gracias, y gracias por todos los chicos en esta página. La respuesta de BalusC es lo que quería. Sin embargo, me pregunto si mi código es recomendado. Aquí está mi contexto de la aplicación: http: //forums.oracle.com/forums/message.jspa? MessageID = 7006003 # 7006003 – hguser

6

Creo que debería usar getActualTypeParameters; como getTypeParameters no se refiere a lo que se ha puesto en su creación de instancias actual en lugar de E, sino a E (para describir cómo está limitado, etc.).

Para obtener el ParameterizedType primero debe usar getGenericSuperclass.

actualización: pero lo anterior sólo funciona si el objeto actual se deriva de una clase genérica con el argumento genérico instancia, como:

class StringList extends ArrayList<String> { 
    public Type whatsMyGenericType() { 
     return ((ParameterizedType)getGenericSuperClass()).getActualTypeParameters()[0]; 
    } 
} 

deberían volver String.class.

+4

Para ser precisos: getGenericSuperclass() le dará resultados significativos solo si la clase no es genérica clase, entonces: nueva LinkedList () .getClass(). getGenericSuperclass() no será suficiente para usted, pero: nueva LinkedList () {}. getClass(). getGenericSuperclass() está bien - notó la diferencia? Al agregar {} creé la subclase de LinkedList. – iirekm

+0

@iirekm verdadero, entonces supongo que no hay forma de que un objeto detecte cómo se declaró de manera refleja, pero podría hacerse "desde afuera" (usando 'Field.getGenericType()', por ejemplo) ... – fortran

2

No creo que pueda obtener el tipo genérico en tiempo de ejecución. El tipo genérico es una restricción que se aplica en tiempo de compilación. Como recuerdo en tiempo de ejecución, no hay diferencia entre una colección genérica y una colección sin un tipo genérico.

+0

No del todo verdadero: lea la solución por 'fortran' y mi comentario al respecto. – iirekm

+2

He he, pensé que una vez también ... La eliminación significa que en tiempo de compilación podrías ignorar las restricciones genéricas ya que el bytecode que ejecuta ambas instancias es el mismo, pero la información sobre cómo se declararon las instancias se mantiene. – fortran

1

El enfoque habitual para solucionar el problema es cambiar ligeramente el código. Defina el constructor en la clase base aceptando el parámetro Class<E>. Asigna este parámetro al campo interno.

En la subclase defina el constructor sin parámetros y llame a super(User.class) desde allí.

De esta manera sabrá la clase de argumento sin demasiada sobrecarga para los clientes de las subclases.

Cuestiones relacionadas