2009-11-14 11 views
19

Me gustaría crear una instancia de una clase especificada utilizando su nombre. Mi código se muestra a continuación.Genéricos y Class.forName

Aparece una advertencia del compilador. ¿Estoy haciendo esto de la manera correcta? ¿Es posible utilizar el nombre de una clase y recuperar una instancia de ese tipo, ya que no creo que haya ninguna forma de que el compilador sepa cuál debe ser el tipo?

public static <T> T create(final String className) { 
    try { 
     final Class<?> clazz = Class.forName(className); 

     //WARNING: Type safety: Unchecked cast from capture#2-of ? to T 
     return (T) create(clazz); 
    } 
    catch (ClassNotFoundException e) { 
     e.printStackTrace(); 
    } 
} 

public static <T> T create(final Class<T> classToCreate) { 
    final Constructor<T> constructor; 
    try { 
     constructor = classToCreate.getDeclaredConstructor(); 
     final T result = constructor.newInstance(); 
     return result; 
    } 
    catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

Gracias

Respuesta

28

creo que el primer método debe ser algo como esto:

public static <T> T create(final String className, Class<T> ifaceClass) 
throws ClassNotFoundException { 
    final Class<T> clazz = Class.forName(className).asSubclass(ifaceClass); 
    return create(clazz); 
} 

No se puede hacer un up-yeso Typecast utilizando un parámetro de tipo ... sin ese tipo molestos -advertencias de seguridad.

Por cierto, si ignora esas advertencias, el método create puede crear una instancia de alguna clase que no sea compatible con el tipo real utilizado por la persona que llama. Es probable que esto lleve a una ClassCastException inesperada más adelante; p.ej. cuando la instancia está asignada


EDIT: @Pascal señala que hay que añadir un encasillado para hacer esta compilación; es decir,

Class<T> clazz = (Class<T>) Class.forName(className).asSubclass(ifaceClass); 

Desafortunadamente, también que añadir una anotación @SuppressWarnings.

+0

Sí, esto es lo más cerca que puede llegar con Java. En cuanto a la creación del objeto real todavía hay algunos trucos que hacer, pero no voy a entrar en los detalles, ya que esos trucos se implementan en mi clase de utilidad que estoy planeando liberar tan pronto como encuentre el correcto canal para difundirlo entre todos los programadores de Java. – Esko

+0

@Esko, considere el código de Google (para versiones publicadas) y/o con Github (para código real) –

+0

Necesita agregar un molde para compilar 'final Class clazz = (Class ) Class.forName (className). asSubclass (ifaceClass); ' –

2

Creo que esto se debe a que Class.forName(..) no está parametrizado para T. Cuando desencadena el autocompletado de eclipse, asume el objeto de retorno clazz.newInstance(). Por lo tanto, conserva el elenco y agrega @SuppressWarnings. Si no utiliza el método correctamente (es decir, String str = Utils.create("java.util.ArrayList");), se producirá un ClassCastException, pero eso es normal.

+0

crear no arrojará una ClassCastException. El código que lo llama podría, pero no necesariamente de inmediato. – meriton

+0

Bueno, sí, pero esto depende de cómo se corrige sus métodos, porque ahora no compilan - ya sea que relanzamientos la excepción, o null volver (lo que hará que la NPE) Salida – Bozho

2

El segundo método está bien.


Pero para la primera, cualquier nombre de clase podría pasarse como parámetro.

El objetivo del método sería instanciar una clase que el código de llamada no conoce en tiempo de compilación, solo lo sabe en tiempo de ejecución.

En estas condiciones, el código de llamada no se puede establecer el parámetro de tipo, por lo que el método no puede ser parametrizado, por lo que el molde no tiene un punto ...

Por lo tanto, yo diría que todo el método no tiene ningún punto de .


Un cierto punto se podría dar al método si el código de llamada sabría que un subtipo de la clase. Eso podría pasar como un parámetro, y el elenco sería posible y significativo.

public static <T> T create(final Class<T> superType, final String className) { 
    try { 
    final Class<?> clazz = Class.forName(className); 
    final Object object = clazz.newInstance(); 
    if (superType.isInstance(object)) { 
     return (T)object; // safe cast 
    } 
    return null; // or other error 
    } // catch and other error code 
} 
+0

el método Class.cast (Objeto). – meriton

+0

(Para obtener el código más corto, y eliminar esa advertencia sin marcar) – meriton

+0

@Meriton Es cierto, si el error optado por informar de un elenco imposible es lanzar una ClassCastException. Por ejemplo, este código devuelve null, una opción diferente ... Debe hacerse una elección ... ;-) – KLE

0

Incluso si usted puede conseguir el código anterior para trabajar, el método newInstance() del constructor asume un constructor sin argumentos.

Si la clase no tiene una i.El constructor de cero argumentos de la clase que está intentando crear ha sido declarado explícitamente privado, o paquete dependiendo de dónde se llama el método, obtendrá una IllegalAccessException. Algo para agregar a su precondición, si lo hace funcionar.

1

No se puede restringir a un parámetro de tipo para contener el tipo llamado className. Por lo tanto, una persona que llama puede suministrar cualquier tipo a su función create (String), que por supuesto no es segura.

Dado que no se puede exigir estáticamente que la instancia devuelta implemente ninguna interfaz (sin que la persona que llama le informe al pasar el objeto Class correspondiente), prescindiré de los genéricos y haré que la persona que llame reproduzca el tipo que espere.

public static Object create(final String className) { 
    try { 
     final Class<?> clazz = Class.forName(className); 
     return create(clazz); 
    } 
    catch (ClassNotFoundException e) { 
     e.printStackTrace(); 
    } 
} 

Una persona que llama puede entonces escribir:

Foo foo = (Foo) create("my.cool.FooBar"); 

en contraposición a

Foo foo = create("my.cool.FooBar", Foo.class); 
0

me encontré con una cosa interesante: Type se puede convertir en Class directamente si no es un tipo genérico . Pero todavía no puedo Class.forName("java.util.List<SomeType>").

import java.lang.reflect.*; 
import java.util.*; 

public class Main { 
    public void func(List<List<String>> listlist, Map<String, List<String>> list, String s) {} 

    public static void main(String[] args) throws ClassNotFoundException { 
     Method m = Main.class.getDeclaredMethods()[1]; 

     Type t0 = m.getGenericParameterTypes()[0]; 
     boolean b0 = t0 instanceof Class; 

     boolean b01 = ((ParameterizedType)t0).getRawType() instanceof Class; 

     Type t1 = m.getGenericParameterTypes()[2]; 
     boolean b1 = t1 instanceof Class; 
    } 
} 

enter image description here