Java Generics - метод Bridge?

Что-то называемое концепцией "метод моста", связанное с Java Generics, заставило меня остановиться в точке и подумать над ней.

Btw, я знаю только, что это происходит на байт-код и недоступен для использования.

Но я очень хочу знать концепцию "метода моста", используемую компилятором Java.

Что именно происходит за кулисами и почему оно используется?

Любая помощь с примером будет принята с благодарностью.

Ответ 1

Это метод, который позволяет классу, расширяющему общий класс или реализующему общий интерфейс (с конкретным параметром типа), все еще использоваться как необработанный тип.

Представьте себе следующее:

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

Это не может использоваться в его исходной форме, передавая два Object для сравнения, поскольку типы компилируются в метод сравнения (вопреки тому, что произойдет, если бы он был типичным параметром типа T, где тип стираться). Вместо этого, за кулисами, компилятор добавляет "метод моста", который выглядит примерно так (это был источник 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);
   }
}

Компилятор защищает доступ к мостовому методу, принуждая, что явные вызовы непосредственно к нему приводят к ошибке времени компиляции. Теперь класс можно использовать и в необработанном виде:

Object a = 5;
Object b = 6;

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

Зачем еще это нужно?

В дополнение к добавлению поддержки явного использования необработанных типов (в основном для обратной совместимости) мосты также необходимы для поддержки стирания стилей. С стиранием типа метод, подобный этому:

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

фактически скомпилирован в байт-код, совместимый с этим:

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

Если метод моста не существовал, и вы передали эту функцию List<Integer> и a MyComparator, вызов в строке с тегами IMPORTANT завершился с ошибкой, так как MyComparator не будет иметь метод под названием compare который принимает два Object s... только один, который принимает два Integer s.

Часто задаваемые вопросы ниже - хорошее чтение.

См. также:

Ответ 2

Если вы хотите понять, зачем вам нужен метод моста, вам лучше понять, что происходит без него. Предположим, что нет метода моста.

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

Обратите внимание, что после стирания set методов в A стал public void set(Object newVal) поскольку нет ограничения на параметр типа T В классе B нет метода, сигнатура которого совпадает с set в A Так что нет переопределения. Следовательно, когда что-то подобное произошло:

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

Полиморфизм здесь не сработает. Помните, что вам нужно переопределить метод родительского класса в дочернем классе, чтобы вы могли использовать родительский класс var для запуска полиморфизма.

То, что делает метод моста, - это незаметное переопределение метода в родительском классе всей информацией от метода с тем же именем, но с другой подписью. С помощью метода бриджа полиморфизм сработал. Хотя на поверхности вы переопределяете метод родительского класса методом другой сигнатуры.

Ответ 3

Это заставляет заметить, что компилятор сообщает, что MyComparator метод:

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

пытается переопределить Comparator<T>

public int compare(T a, T b);

из объявленного типа Comparator<Integer>. В противном случае MyComparator compare будет обработан компилятором как дополнительный (перегруженный), а не метод переопределения. И как таковой, для него не будет создан мостовой метод.