Для чего используется Method.isBridge?

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

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

3 ответа

Решение

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

Вы можете найти в этом классе 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();

Показанный там пример (цитируемый из 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 интерфейс, если не создан метод моста. Метод моста позволяет всему типизированному коду использовать это 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");

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

protected abstract class Super {
    public void m() {}
}
public class Sub extends Super {}
assert Sub.class.getMethod("m").isBridge();
Другие вопросы по тегам