76 votes

Java Generics - Méthode de transition ?

Un concept appelé "méthode passerelle" lié aux génériques Java m'a fait m'arrêter à un moment donné et y réfléchir.

Entre-temps, je sais seulement que cela se produit au niveau du niveau du bytecode et n'est pas disponible pour que nous l'utilisions.

Mais je suis impatient de connaître le concept derrière la "méthode du pont" utilisée par le compilateur Java.

Que se passe-t-il exactement en coulisses et pourquoi l'utilise-t-on ?

Toute aide sous la forme d'un exemple serait grandement appréciée.

14 votes

Je ne peux pas l'expliquer plus clairement que ça : stas-blogspot.blogspot.com/2010/03/ (qui se trouve être le 1er résultat de google)

102voto

Mark Peters Points 42201

Il s'agit d'une méthode qui permet à une classe étendant une classe générique ou implémentant une interface générique (avec un paramètre de type concret) d'être toujours utilisée comme un type brut.

Imaginez ceci :

public class MyComparator implements Comparator<Integer> {
   public int compare(Integer a, Integer b) {
      //
   }
}

Il ne peut pas être utilisé dans sa forme brute, en passant deux Object à comparer, car les types sont compilés dans la méthode de comparaison (contrairement à ce qui se passerait s'il s'agissait d'un paramètre de type générique T, où le type serait effacé). Au lieu de cela, en coulisses, le compilateur ajoute une "méthode de pont", qui ressemble à quelque chose comme ceci (s'il s'agissait d'une source Java) :

public class MyComparator implements Comparator<Integer> {
   public int compare(Integer a, Integer b) {
      //
   }

   //THIS is a "bridge method"
   public int compare(Object a, Object b) {
      return compare((Integer)a, (Integer)b);
   }
}

Le compilateur protège l'accès à la méthode bridge, en faisant en sorte que les appels explicites à cette méthode entraînent une erreur de compilation. Maintenant, la classe peut également être utilisée dans sa forme brute :

Object a = 5;
Object b = 6;

Comparator rawComp = new MyComparator();
int comp = rawComp.compare(a, b);

Pour quelle autre raison serait-elle nécessaire ?

Outre l'ajout de la prise en charge de l'utilisation explicite des types bruts (principalement pour des raisons de compatibilité ascendante), les méthodes de pontage doivent également prendre en charge l'effacement des types. Avec l'effacement des types, une méthode comme celle-ci :

public <T> T max(List<T> list, Comparator<T> comp) {
   T biggestSoFar = list.get(0);
   for ( T t : list ) {
       if (comp.compare(t, biggestSoFar) > 0) {
          biggestSoFar = t;
       }
   }
   return biggestSoFar;
}

est en fait compilé en bytecode compatible avec celui-ci :

public Object max(List list, Comparator comp) {
   Object biggestSoFar = list.get(0);
   for ( Object  t : list ) {
       if (comp.compare(t, biggestSoFar) > 0) {  //IMPORTANT
          biggestSoFar = t;
       }
   }
   return biggestSoFar;
}

Si la méthode de pontage n'existait pas et que vous passiez une méthode List<Integer> et un MyComparator à cette fonction, l'appel à la ligne marquée IMPORTANT échouerait puisque MyComparator n'aurait pas de méthode appelée compare qui prend deux Object s...seulement un qui prend deux Integer s.

La FAQ ci-dessous est une bonne lecture.

Voir aussi :

6 votes

Également pour les types de retour covariants, même en l'absence de génériques.

2 votes

@paulmurray : qu'est-ce que "equals(T o)" ?

0 votes

@Tom Hawtin - tackline pouvez-vous clarifier le type de retour covariant ?

11voto

ch48h2o Points 45

Si vous voulez comprendre pourquoi vous avez besoin de la méthode du pont, vous devez comprendre ce qui se passe sans elle. Supposons qu'il n'y ait pas de méthode du pont.

class A<T>{
  private T value;
  public void set(T newVal){
    value=newVal
  }
}

class B extends A<String>{
  public void set(String newVal){
    System.out.println(newVal);
    super.set(newVal);
  }
}

Remarquez qu'après l'effacement, la méthode set sur A est devenu public void set(Object newVal) puisqu'il n'y a pas de limite au paramètre Type T . Il n'y a pas de méthode dans la classe B dont la signature est la même que set sur A . Il n'y a donc pas de dérogation. Donc, quand quelque chose comme ça arrive :

A a=new B();
a.set("Hello World!");

Le polymorphisme ne fonctionnera pas ici. Rappelez-vous que vous devez surcharger la méthode de la classe parent dans la classe enfant afin que vous puissiez utiliser les variables de la classe parent pour déclencher le polymorphisme.

La méthode de pontage consiste à remplacer silencieusement la méthode de la classe parente par toutes les informations d'une méthode portant le même nom mais une signature différente. Avec l'aide de la méthode bridge, le polymorphisme fonctionne. Bien qu'en apparence, vous remplacez la méthode de la classe parente par une méthode de signature différente.

0voto

Callistus Points 11

Il est intéressant de noter que le compilateur déduit que MyComparator La méthode de l'entreprise :

public int compare(Integer a, Integer b) {/* code */}

essaie de passer outre Comparator<T> 's

public int compare(T a, T b);

du type déclaré Comparator<Integer> . Autrement, MyComparator 's compare serait traitée par le compilateur comme une méthode supplémentaire (de surcharge), et non pas de surcharge. Et en tant que telle, aucune méthode de pont ne serait créée pour elle.

0voto

Amos Points 175

Comme l'indique cet article et cet article la raison principale de la méthode du pont Java est Effacement de type et Polymorphisme .

Prenons la classe ArrayDeque ( code source ) à titre d'exemple, il contient un clone() comme ci-dessous, car la classe ArrayDeque met en œuvre la Cloneable il doit donc remplacer l'interface Object.clone() méthode.

public class ArrayDeque<E> extends AbstractCollection<E>
                        implements Deque<E>, Cloneable, Serializable
{

  public ArrayDeque<E> clone() {
    ....
  }
}

UML Hierarchy Diagram of ArrayDeque

Mais le problème est que le type de retour de ArrayDeque.clone() est ArrayDeque<E> et elle ne correspondait pas à la signature de la méthode définie dans le modèle parent. Object.clone() et dans Objet.java le type de retour est Object à la place.

public class Object {

    protected native Object clone() throws CloneNotSupportedException;
}

La non-concordance des types de retour est un problème pour les services suivants Polymorphisme . Ainsi, dans le fichier de résultat compilé ArrayDeque.class le compilateur Java a généré deux clone() l'une correspond à la signature du code source, l'autre à la signature de la classe parente. Object.clone() .

  1. clone() La méthode retourne ArrayDeque<E> qui est généré sur la base du code source correspondant.
  2. clone() La méthode retourne Object qui est généré sur la base de Object.clone() . Cette méthode ne fait rien d'autre qu'appeler l'autre clone() méthode. Et, cette méthode est étiquetée comme ACC_BRIDGE ce qui indique que cette méthode est générée par le compilateur pour le pont.

UML diagram generated based on ArrayDeque.class

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