Для чего используется Method.isBridge? - PullRequest
27 голосов
/ 14 ноября 2008

Во время навигации по классу java.lang.reflect.Method я наткнулся на метод isBridge. Его Javadoc говорит, что он возвращает true, только если спецификация Java объявляет метод как true.

Пожалуйста, помогите мне понять, для чего это используется! Может ли пользовательский класс объявить свой метод как мост, если требуется?

Ответы [ 3 ]

27 голосов
/ 14 ноября 2008

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

В этом классе вы можете найти BridgeMethodResolver способ получения фактического метода, на который ссылается «метод моста».

См. Создание кадра, Синхронизация, Управление передачей :

В качестве примера такой ситуации рассмотрим объявления:

class C<T> { abstract T id(T x); }
class D extends C<String> { String id(String x) { return x; } }

Теперь, с помощью вызова

C c = new D();
c.id(new Object()); // fails with a ClassCastException

Стирание фактического вызываемого метода, D.id(String) отличается своей сигнатурой от сигнатуры объявления метода времени компиляции, C.id(Object). Первый принимает аргумент типа String, а второй принимает аргумент типа Object. Вызов завершается неудачно с ClassCastException, прежде чем тело метода будет выполнено.

Такие ситуации могут возникнуть, только если программа выдает непроверенное предупреждение ( §5.1.9 ).

Реализации могут применять эту семантику путем создания мостовых методов. В приведенном выше примере следующий метод моста будет создан в классе D:

Object id(Object x) { return id((String) x); }

Это метод, который фактически будет вызываться виртуальной машиной Java в ответ на вызов c.id(new Object()), показанный выше, и он будет выполнять приведение и сбой, как требуется.

См. Также Мост :

, как упоминалось в комментарии, методы моста также необходимы для ковариантного переопределения :

  • В Java 1.4 и более ранних версиях один метод может переопределять другой, если подписи совпадают точно.
  • В Java 5 метод может переопределить другой, если аргументы совпадают точно , но с типом возврата переопределяющего метода, если это подтип типа возврата другого способ.

Как правило, метод Object clone() может быть переопределен MyObject clone(), но компилятор сгенерирует метод моста:

public bridge Object MyObject.clone();
3 голосов
/ 19 мая 2010

Показанный там пример (цитируемый из JLS) звучит так, будто методы моста используются только в ситуациях, когда используются необработанные типы. Поскольку это не так, я подумал, что приведу пример, в котором методы моста используются для полностью корректного типа универсального кода.

Рассмотрим следующий интерфейс и функцию:

public static interface Function<A,R> {
    public R apply (A arg);
}
public static <A, R> R applyFunc (Function<A,R> func, A arg) {
    return func.apply(arg);
}

Если вы используете этот код следующим образом, используется метод моста:

Function<String, String> lower = new Function<String, String>() {
    public String apply (String arg) {
        return arg.toLowerCase();
    }
};
applyFunc(lower, "Hello");

После удаления интерфейс Function содержит метод apply(Object)Object (который можно подтвердить, декомпилировав байт-код). Естественно, если вы посмотрите на декомпилированный код для applyFunc, вы увидите, что он содержит вызов apply(Object)Object. Object является верхней границей переменных типа, поэтому никакая другая сигнатура не будет иметь смысла.

Таким образом, когда анонимный класс создается с помощью метода apply(String)String, он фактически не реализует интерфейс Function, если не создан метод моста. Метод bridge позволяет всему типизированному коду использовать эту реализацию Function.

Интересно, что только если класс реализует какой-либо другой интерфейс с сигнатурой apply(String)String и только если метод вызывается через ссылку этого типа интерфейса, компилятор когда-либо будет генерировать вызов с этой сигнатурой.

Даже если у меня есть следующий код:

Function<String, String> lower = ...;
lower.apply("Hello");

Компилятор по-прежнему отправляет вызов apply(Object)Object.

На самом деле есть еще один способ заставить компилятор вызывать apply(String)String, но он использует магический тип, присваиваемый анонимному выражению создания класса, который иначе не может быть записан:

new Function<String, String>() {
    public String apply (String arg) {
        return arg.toLowerCase();
    }
}.apply("Hello");
0 голосов
/ 26 августа 2013

Другой случай, с которым я столкнулся, не имеет ничего общего с генериками:

protected abstract class Super {
    public void m() {}
}
public class Sub extends Super {}
assert Sub.class.getMethod("m").isBridge();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...