Java Génériques - Pont méthode?

Quelque chose appelé le concept de "méthode de pont" lié aux génériques Java m'a fait m'arrêter à un moment et y réfléchir.

Btw, je sais seulement que cela se produit à la bytecode niveau et n'est pas disponible pour l'utiliser.

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

Que se passe-t-il exactement dans les coulisses et pourquoi il est utilisé?

Toute aide avec un exemple serait grandement appréciée.

51
demandé sur Charles 2011-02-15 20:48:40

3 réponses

C'est une méthode qui permet à une classe d'étendre une classe générique ou d'implémenter une interface générique (avec un paramètre de type concret) d'être toujours utilisée comme type brut.

Imaginez ceci:

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

Cela ne peut pas être utilisé sous sa forme brute, en passant deux Object s à comparer, car les types sont compilés dans la méthode compare (contrairement à ce qui se passerait s'il s'agissait d'un paramètre de type générique T, où le type serait effacé). Donc, au lieu de cela, dans les coulisses, le compilateur ajoute un " pont méthode", qui ressemble à ceci (était-Il 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 imposant que les appels explicites directement à celle-ci entraînent une erreur de compilation. Maintenant, la classe peut également être utilisée sous sa forme brute:

Object a = 5;
Object b = 6;

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

Pourquoi est-il nécessaire?

En plus d'ajouter le support pour l'utilisation explicite des types bruts (qui est principalement pour la compatibilité ascendante), les méthodes de pont sont également nécessaires pour prendre en charge l'effacement de type. Avec le type erasure, 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 ceci:

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 bridge n'existait pas et que vous avez passé un List<Integer> et un MyComparator à cette fonction, l'appel à la ligne étiquetée IMPORTANT échouerait puisque MyComparator n'aurait aucune méthode appelée comparequi prend deux Object s...un seul qui prend deux Integers.

La FAQ ci-dessous est une bonne lecture.

Voir Aussi:

63
répondu Mark Peters 2017-05-23 11:46:31

Il est intéressant de noter que le compilateur déduit que la méthode de MyComparator:

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

Tente de remplacer Comparator<T> ' s

public int compare(T a, T b);

Du type déclaré Comparator<Integer>. Sinon, MyComparator compare serait traité par le compilateur comme une méthode supplémentaire (surcharge), et non surchargée. Et en tant que tel, n'aurait pas de méthode de pont créée pour cela.

0
répondu Callistus 2014-11-02 22:45:08

Si vous voulez comprendre pourquoi vous avez besoin de la méthode bridge, vous comprenez mieux ce qui se passe sans elle. Supposons qu'il n'y ait pas de méthode de 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);
  }
}

Notez qu'après l'effacement, la méthode set dans A est devenue public void set(Object newVal) puisqu'il n'y a pas de limite sur le paramètre de Type T. Il N'y a pas de méthode dans la Classe B dont la signature est la même que set dans A. Il N'y a donc pas de substitution. Par conséquent, quand quelque chose comme ça est arrivé:

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

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

Ce Que fait la méthode bridge est de remplacer silencieusement la méthode dans la classe parent avec toutes les informations d'une méthode avec le même nom mais une signature différente. Avec l'aide de la méthode bridge, le polymorphisme a fonctionné. Bien que sur la surface, vous remplacez la méthode de classe parent par une méthode de signature différente.

0
répondu ch48h2o 2018-01-30 09:59:30