Java Generics - метод моста? - PullRequest
       10

Java Generics - метод моста?

59 голосов
/ 15 февраля 2011

Нечто, называемое концепцией «метода моста», связанное с Java Generics, заставило меня остановиться и задуматься над этим.

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

Но мне не терпится узнать концепцию «метода моста», используемого компилятором Java.

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

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

Ответы [ 3 ]

74 голосов
/ 15 февраля 2011

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

Представьте себе:

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

Это нельзя использовать в необработанном виде, передавая два Object s для сравнения, потому что типы компилируются в метод сравнения (вопреки тому, что произошло бы, если бы это был параметр универсального типа 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> и MyComparator для этой функции вызов в строке, помеченной IMPORTANT, будет неудачным, поскольку MyComparator не будет иметь метода с именем compare, который занимает два Object с ... только один, который принимает два Integer s.

Приведенный ниже FAQ является хорошим чтением.

См. также:

3 голосов
/ 30 января 2018

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

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 для запуска полиморфизма.

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

0 голосов
/ 03 ноября 2014

Интересно отметить, что компилятор выводит , что метод MyComparator:

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

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

public int compare(T a, T b);

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...