2008-12-19 17 views
5

Tengo un método fetchObjects(String) que se espera que devuelva una matriz de Contract objetos comerciales. El parámetro className me dice qué tipo de objetos comerciales debo devolver (por supuesto, esto no tiene sentido en este caso interpretado porque ya dije que devolveré Contract s, pero básicamente es la situación que tengo en mi situación real). Así que obtengo el conjunto de entradas de algún lugar y cargo la clase de las entradas de la colección (cuyo tipo está especificado en className).Transmisión a una clase que se determina en tiempo de ejecución

Ahora necesito construir la matriz para devolver, entonces uso el método SettoArray(T[]). Usando la reflexión, me construyo una matriz de Contratos vacía. Pero, esto me da un valor de tipo estático Object! Así que, a continuación, tengo que convertirlo al tipo apropiado, que en este caso es Contract[] (ver la parte "asterisco subrayado" en el listado a continuación).

Mi pregunta es: ¿Hay alguna manera, y cómo, para echar a Contract[] como lo hago en el perfil, pero determinar el tipo de los elementos de la matriz (Contract) sólo a través de className (o entriesType)? En otras palabras, lo que me gustaría hacer es básicamente transmitir de esta forma: (entriesType[]) valueWithStaticTypeObject, donde entriesType se reemplaza por la clase especificada a través del parámetro classname, es decir, Contract.

¿Es esto de alguna manera inherentemente imposible, o se puede hacer de alguna manera? Tal vez usando genéricos?

package xx.testcode; 

import java.util.HashSet; 
import java.util.Set; 

class TypedArrayReflection { 

    public static void main(String[] args) { 
     try { 
      Contract[] contracts = fetchObjects("Contract"); 
      System.out.println(contracts.length); 
     } catch (ClassNotFoundException e) {} 
    } 

    static Contract[] fetchObjects(String className) throws ClassNotFoundException { 
     Class<?> entriesType = Class.forName("xx.testcode."+className); 
     Set<?> entries = ObjectManager.getEntrySet(className); 
     return entries.toArray( 
       (Contract[]) java.lang.reflect.Array.newInstance(
       /********/   entriesType, entries.size())); 
    } 
} 

class Contract { } // business object 

class ObjectManager { 
    static Set<?> getEntrySet(String className) { 
     if (className.equals("Contract")) 
      return new HashSet<Contract>(); 
     return null; // Error 
    } 
} 

Thanks.


Actualización: Utilizando el método de tipo seguro toArray, tomada de CodeIdol, he actualizado mi fetchObjects método así:

static Contract[] fetchObjects(String className) throws ClassNotFoundException { 
    Class<?> entriesType = Class.forName("xx.testcode."+className); 
    Set<?> entries = ObjectManager.getEntrySet(className); 
    return toArray(entries, entriesType); // compile error 
    // -> "method not applicable for (Set<capture#3-of ?>, Class<capture#4-of ?>)" 
} 

public static <T> T[] toArray(Collection<T> c, Class<T> k) { 
    T[] a = (T[]) java.lang.reflect.Array.newInstance(k, c.size()); 
    int i = 0; 
    for (T x : c) 
     a[i++] = x; 
    return a; 
} 

¿Qué necesito hacer para deshacerse del error del compilador citado en el comentario? ¿Tengo que especificar absolutamente Set<Contract> en el tipo de devolución de mi método getEntrySet para que esto pueda funcionar? Gracias por cualquier puntero.

+0

¿Cómo se maneja cuando la clase entriesType no es una subclase de Contrato? –

+0

entriesType es * no * necesariamente un subtipo de Contrato. Podría ser Pet, Friend, Hobby, cualquier cosa de la que tengas una colección. En mi caso de ejemplo, un Cliente tendría una colección de Contratos. –

Respuesta

7

Puede usar la clase como el parámetro en lugar del nombre de la clase.

static <T extends Contract> T[] buildArray(Class<T> clazz){ 
    ArrayList<T> l=new ArrayList<T>(); 
    return l.toArray((T[]) java.lang.reflect.Array.newInstance(clazz, l.size())); 
    } 

EDIT: (después de leer los comentarios de Yang)

No, no puede utilizar el tipo genérico con el valor de una variable.

+0

¡Esto es bastante impresionante! Sin embargo, la cosa es que * no * quiero especificar con rigor la clase de contrato (el objeto de negocio podría ser House o Pet, dependiendo del parámetro de método className).La clase correcta debe determinarse a partir del parámetro className. –

+0

Dado que la persona que llama debería saber qué tipo de objeto espera, de todos modos, ¿por qué no pasar el objeto Class en lugar del nombre? No entiendo qué ventaja tiene el nombre. –

+0

Cf. mi comentario a la respuesta de Milhous. –

1

Por qué no utilizar

static <T> T[] buildArray(Class<T> clazz){ 
ArrayList<T> l=new ArrayList<T>(); 
return l.toArray((T[]) java.lang.reflect.Array.newInstance(clazz, l.size())); 
} 

Nota. Modificado el código desde arriba.

+0

Creo que puede querer que los genéricos se basen en el valor de "String className", sin un tipo/genéricos/class parámetro. –

+0

Exactamente. En el escenario real, necesito usar la reflexión para obtener el nombre del tipo, p. Obtengo "Contrato" de la reflexión y luego cargo la clase con este nombre. –

+3

No puede encasillar a un tipo desconocido en tiempo de compilación. Typecasting es un problema de tiempo de compilación. Generics es solo una forma de parametrizar el typecast, para permitir que el compilador propague información de tipo de una parte del código a otra. Pero aún necesita ser conocido por el compilador. –

Cuestiones relacionadas