37 votes

Méthode générique en Java sans argument générique

En C #, je peux faire ceci:

 //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>();
 

Mais pour une raison quelconque, je ne peux pas le faire fonctionner en Java.

La chose que je veux faire est de créer une méthode statique sur une superclasse, afin que les sous-classes puissent être converties en 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/>");
 

54voto

Tom Hawtin - tackline Points 82671
public static <T> T fromXml(Class<T> clazz, String xml) {

Appelé aussi:

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

ou plus explicitement:

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

Pour être encore plus de confusion, vous pouvez avoir des constructeurs à la fois construire un type générique et ont un paramètre générique eux-mêmes. Ne peut pas se souvenir de la syntaxe et ne l'ai jamais vu utilisé dans la colère (vous êtes probablement mieux avec un statique de la méthode de création de toute façon).

Le casting (T) est dangereux, et vous ne pouvez pas écrire T.class. Ainsi, inclure la T.class comme un argument (comme JAXBContext.newInstance t) et de lancer une exception si le type est mauvais.

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
         );
    }
}

Je crois que la prochaine version de JAXB (en 6u14?) a quelques méthodes pratiques pour ce genre de chose dans l' JAXB classe.

5voto

Avi Points 14468

En Java, les génériques sont des données de compilation uniquement, qui sont perdues au moment de l'exécution. Donc, si vous appeliez une telle méthode, la JVM n'aurait aucun moyen de savoir ce qu'était T.class . La façon normale de contourner cela est de passer un objet d'instance de classe en tant que paramètre à la méthode, comme ceci:

 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/>");
 

3voto

cadrian Points 4102

J'ai fait quelque chose de similaire il y a quelque temps. Vous avez besoin d'un code d'introspection intelligent. Regardez cet excellent article .

(pour éliminer toute confusion: cet article n'est pas le mien, mais je l'ai trouvé en travaillant sur le sujet)

1voto

Bogdan Points 776

J'ai peur de ce que vous essayez de faire tout simplement pas de travail en Java. Ne pas être en mesure de créer de nouvelles instances de types génériques est l'un de ces "must have" fonctionnalités .NET fournis, alors que Java est tout simplement absent. Cela conduit à des "solutions" comme ceux que l'a suggéré plus tôt. Découvrez l' ArrayList.toArray(T) comme référence la façon dont cela peut être fait: essentiellement, vous aurez à passer une référence à un objet que vous essayez de créer afin que vous sachiez quelle classe instancier au moment de l'exécution. C'est le code de l' 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;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X