tipos metodos lesson instanciar genericos genericas generic datos clases arreglos java generics

metodos - Método genérico en Java sin argumento genérico



set border java (5)

En Java, los genéricos son datos de tiempo de compilación, que se pierden en el tiempo de ejecución. Entonces, si T.class un método como ese, la JVM no tendría forma de saber qué T.class era T.class La forma normal de evitar esto es pasar un objeto de instancia de clase como un parámetro al método, como este:

public static <T> T fromXml(Class<T> clazz, String xml) { try { JAXBContext context = JAXBContext.newInstance(clazz); Unmarshaller um = context.createUnmarshaller(); return (T)um.unmarshal(new StringReader(xml)); } catch (JAXBException je) { throw new RuntimeException("Error interpreting XML response", je); } } fromXml(SomeSubObject.class, "<xml/>");

En C # puedo hacer esto:

//This is C# static T SomeMethod<T>() where T:new() { Console.WriteLine("Typeof T: "+typeof(T)); return new T(); } //And call the method here SomeMethod<SomeClassName>();

Pero por alguna razón no puedo hacer que funcione en Java.

Lo que quiero hacer es crear un método estático en una superclase, para que las subclases se puedan convertir a XML.

//This is Java, but doesn''t work public static T fromXml<T>(String xml) { try { JAXBContext context = JAXBContext.newInstance(T.class); Unmarshaller um = context.createUnmarshaller(); return (T)um.unmarshal(new StringReader(xml)); } catch (JAXBException je) { throw new RuntimeException("Error interpreting XML response", je); } } //Also the call doesn''t work... fromXml<SomeSubObject>("<xml/>");


La respuesta actual es más segura, pero está técnicamente desactualizada porque en java7 y más allá no necesitas el argumento "Class clazz". El tipo se deduce del tipo de retorno esperado. Acabas de recibir una advertencia sobre el lanzamiento inseguro.

public class Main { private static class Dog { public String toString() { return "dog"; } } private static class Cat { public String toString() { return "cat"; } } public static void main(String[] args) { Cat cat = parse("cat"); Dog dog = parse("dog"); System.out.println("the cat object is a " + cat); System.out.println("the dog object is a " + dog); } private static Object untypedParse(String stringToParse) { if(stringToParse.equals("dog")) { return new Dog(); } else if(stringToParse.equals("cat")) { return new Cat(); } else { throw new RuntimeException("not expected"); } } public static <T> T parse(String stringToParse) { return (T)untypedParse(stringToParse); } } ~/test/generics$ javac Main.java Note: Main.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. ~/test/generics$ java Main the cat object is a cat the dog object is a dog


Métodos como Collections.emptySet() Java tienen una firma como esta:

public static final <T> Set<T> emptySet()

Y se llaman así:

Set<Foo> foos = Collections.<Foo>emptySet();

El método anyObject() de anyObject() es otro ejemplo. Personalmente, no creo que ninguna de las dos sintaxis sea increíble. Pasar el tipo como un argumento de método funciona pero siempre se sintió kludgy. Proporcionar el parámetro en la forma en que emptySet() parece más limpio, pero no siempre es evidente que un método permita especificar un tipo.


Me temo que lo que estás tratando de hacer simplemente no funcionará en Java. No poder crear nuevas instancias de tipos genéricos es una de esas características "imprescindibles" que proporciona .NET mientras que simplemente falta Java. Esto lleva a "soluciones" como las sugeridas anteriormente. Consulte ArrayList.toArray(T) como referencia de cómo se puede hacer esto: esencialmente, tendrá que pasar una referencia a un objeto que está intentando crear para saber qué clase crear en el momento de la ejecución. Este es el código de ArrayList.toArray(T) :

public <T> T[] toArray(T[] a) { if (a.length < size) { a = (T[]) java.lang.reflect.Array. newInstance(a.getClass().getComponentType(), size); } System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) { a[size] = null; } return a; }


public static <T> T fromXml(Class<T> clazz, String xml) {

Llamado:

Thing thing = fromXml(Thing.class, xml);

o más explícitamente:

Thing thing = MyClass.<Thing>fromXml(Thing.class, xml);

Para ser aún más confuso puede tener constructores que construyen un tipo genérico y tienen un parámetro genérico ellos mismos. No puedo recordar la sintaxis y nunca la he visto usar con enojo (de todos modos, es mejor que tengas un método de creación estático).

El elenco (T) no es seguro, y no puedes escribir T.class. Por lo tanto, incluya el T.class como argumento (como hace JAXBContext.newInstance ) y JAXBContext.newInstance una excepción relevante si el tipo es incorrecto.

public static <T> T fromXml(Class<T> clazz, String xml) { try { JAXBContext context = JAXBContext.newInstance(clazz); Unmarshaller um = context.createUnmarshaller(); Object obj = um.unmarshal(new StringReader(xml)); try { return clazz.cast(obj); } catch (ClassCastException exc) { throw new RelevantException( "Expected class "+clazz+ " but was "+obj.getClass() ); } } catch (JAXBException exc) { throw new RelevantException( "Error unmarshalling XML response", exc ); } }

Creo que la próxima versión de JAXB (en 6u14?) Tiene algunos métodos de conveniencia para este tipo de cosas en la clase JAXB .