web-dev-qa-db-fra.com

Génériques Java: type générique défini comme type de retour uniquement

Je suis en train de regarder du code GXT pour GWT et j'ai découvert cette utilisation de Generics que je ne trouve pas dans les tutoriels Java. Le nom de la classe est com.extjs.gxt.ui.client.data.BaseModelData si vous voulez regarder tout le code. Voici les parties importantes:

private RpcMap map;

public <X> X get(String property) {
  if (allowNestedValues && NestedModelUtil.isNestedProperty(property)) {
    return (X)NestedModelUtil.getNestedValue(this, property);
  }
  return map == null ? null : (X) map.get(property);
}

X n'est défini nulle part ailleurs dans la classe ou ailleurs dans la hiérarchie, et lorsque je clique sur "go to déclaration" dans Eclipse, il passe simplement au <X> dans la signature de la méthode publique.

J'ai essayé d'appeler cette méthode avec les deux exemples suivants pour voir ce qui se passe:

public Date getExpiredate() {
    return  get("expiredate");
}

public String getSubject() {
    return  get("subject");
}

Ils compilent et ne montrent aucune erreur ou avertissement. Je penserais au moins que je devrais faire un casting pour que cela fonctionne.

Cela signifie-t-il que les génériques permettent une valeur de retour magique qui peut être n'importe quoi et va simplement exploser au moment de l'exécution? Cela semble aller à l'encontre de ce que les génériques sont censés faire. Quelqu'un peut-il m'expliquer cela et éventuellement me donner un lien vers une documentation qui l'explique un peu mieux? J'ai parcouru les 23 pages pdf de Sun sur les génériques et chaque exemple de valeur de retour est défini au niveau de la classe ou dans l'un des paramètres transmis.

64
Jason Tholstrup

La méthode retourne un type de ce que vous attendez (le <X> est défini dans la méthode et est absolument non lié).

Ceci est très très dangereux car rien n’indique que le type de retour corresponde réellement à la valeur renvoyée.

Le seul avantage est que vous n'avez pas à convertir la valeur de retour de telles méthodes de recherche génériques pouvant renvoyer n'importe quel type.

Je dirais: utilisez ces constructions avec précaution, car vous perdez à peu près toute la sécurité de type et gagnez simplement le fait que vous n'avez pas à écrire une distribution explicite à chaque appel de get().

Et oui: il s’agit en réalité d’une magie noire qui explose au moment de l’exécution et brise toute l’idée de ce que les génériques devraient accomplir.

53
Joachim Sauer

Le type est déclaré sur la méthode. C'est ce que "<X>" signifie. Le type est alors limité à la méthode et est pertinent pour un appel particulier. La raison pour laquelle votre code de test est compilé est que le compilateur essaie de déterminer le type et ne se plaindra que s'il ne le peut pas. Il y a des cas où vous devez être explicite. 

Par exemple, la déclaration pour Collections.emptySet() est

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

Dans ce cas, le compilateur peut deviner:

Set<String> s = Collections.emptySet();

Mais s'il ne le peut pas, vous devez taper:

Collections.<String>emptySet();
37
sblundy

J'essayais simplement de comprendre la même chose avec une classe GXT. Plus précisément, j'essayais d'appeler une méthode avec la signature de:

class Model {
    public <X> X get(String property) { ... }
}

Pour appeler la méthode ci-dessus à partir de votre code et le convertir en chaîne en chaîne, procédez comme suit:

public String myMethod(Data data) {
    Model model = new Model(data);
    return model.<String>get("status");
}

Le code ci-dessus appelle la méthode get et lui indique que le type renvoyé par X doit être renvoyé sous forme de chaîne.

Dans le cas où la méthode est dans la même classe que vous, j'ai constaté que je devais l'appeler avec un "this". Par exemple:

this.<String>get("status");

Comme d’autres l’ont dit, c’est plutôt bâclé et dangereux pour l’équipe GXT.

5
Kyrra

BaseModelData déclenche des avertissements non contrôlés lors de la compilation, car elle est dangereuse. Utilisé de cette manière, votre code lève une exception ClassCastException au moment de l'exécution, même s'il ne contient aucun avertissement.

public String getExpireDate() {
  return  get("expiredate");
}
2
erickson

Note intéressante, de RpcMap (GXT API 1.2)

get en-tête:

public Java.lang.Object get(Java.lang.Object key)

Avoir un paramètre générique de <X> non instancié a le même effet, sauf que vous n'avez pas à dire "Object" partout. Je suis d'accord avec l'autre affiche, c'est bâclé et un peu dangereux.

2
Rich

Oui, c'est dangereux. Normalement, vous protégeriez ce code de la manière suivante:

<X> getProperty(String name, Class<X> clazz) {
   X foo = (X) whatever(name);
   assert clazz.isAssignableFrom(foo);
   return foo;
}

String getString(String name) {
  return getProperty(name, String.class);
}

int getInt(String name) {
  return getProperty(name, Integer.class);
}
0
paulmurray