- Оригинальный адрес:Java bridge methods explained
- Оригинальный автор:STAS
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:kezhenxu94
- Корректор:
Метод моста в Java — это синтетический метод, необходимый для реализации определенных функций языка Java. Наиболее известными примерами являются ковариантные типы возвращаемых значений и универсальное стирание, которые приводят к тому, что параметры метода базового класса несовместимы с типами параметров фактически вызываемого метода.
Взгляните на следующие примеры:
public class SampleOne {
public static class A<T> {
public T getT() {
return null;
}
}
public static class B extends A<String> {
public String getT() {
return null;
}
}
}
На самом деле это пример ковариантного возвращаемого типа,Общее стираниеЗатем он станет фрагментом кода, подобным следующему:
public class SampleOne {
public static class A {
public Object getT() {
return null;
}
}
public static class B extends A {
public String getT() {
return null;
}
}
}
После декомпиляции скомпилированного байт-кода классB
будет что-то вроде этого:
public class SampleOne$B extends SampleOne$A {
public SampleOne$B();
...
public java.lang.String getT();
Code:
0: aconst_null
1: areturn
public java.lang.Object getT();
Code:
0: aload_0
1: invokevirtual #2; // 调用 getT:()Ljava/lang/String;
4: areturn
}
Как видно из вышеизложенного, появился новыйСинтетическийметодjava.lang.Object getT()
, которого нет в исходном коде. Этот метод действует как мост, все, что он делает, это делегирует вызовы самому себе методу.jva.lang.String getT()
. Это должен сделать компилятор, потому что в методах JVM тип возвращаемого значения также является частью сигнатуры метода, а создание методов моста — это именно то, как реализуются ковариантные возвращаемые типы.
Теперь взгляните на следующий пример, связанный с дженериками:
public class SampleTwo {
public static class A<T> {
public T getT(T args) {
return args;
}
}
public static class B extends A<String> {
public String getT(String args) {
return args;
}
}
}
скомпилированный классB
Это будет выглядеть так:
public class SampleThree$B extends SampleThree$A{
public SampleThree$B();
...
public java.lang.String getT(java.lang.String);
Code:
0: aload_1
1: areturn
public java.lang.Object getT(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: checkcast #2; //class java/lang/String
5: invokevirtual #3; //Method getT:(Ljava/lang/String;)Ljava/lang/String;
8: areturn
}
Метод моста здесь переопределяет базовый классA
вместо того, чтобы просто делегировать вызовы самому себе базовому классу со строковым параметромA
метод, а также выполняетjava.lang.String
Обнаружение преобразования типа (#2). Это означает, что если вы запустите код, подобный следующему, игнорируя предупреждение компилятора «непроверено», результатом будет исключение, созданное методом моста.ClassCastException
.
A a = new B();
a.getT(new Object()));
Приведенные выше примеры являются двумя наиболее известными вариантами использования методов моста, но есть по крайней мере один вариант использования, когда методы моста используются для «изменения» видимости базового класса. Рассмотрим следующий пример кода и предположим, нужно ли компилятору создать метод моста:
package samplefour;
public class SampleFour {
static class A {
public void foo() {
}
}
public static class C extends A {
}
public static class D extends A {
public void foo() {
}
}
}
если вы декомпилируетеC
класс, вы увидите, что естьfoo
метод, который переопределяет метод базового класса и делегирует вызовы самому себе (метод базового класса):
public class SampleFour$C extends SampleFour$A{
...
public void foo();
Code:
0: aload_0
1: invokespecial #2; //Method SampleFour$A.foo:()V
4: return
}
Компилятору нужен такой метод, потому чтоA
класс не является публичным, вA
Класс не виден за пределами пакета, в котором он находится, ноC
Класс является общедоступным, и все методы, которые он наследует, должны быть видны за пределами пакета, в котором он находится. должны знать о том,D
В классе не будет сгенерирован метод моста, потому что он переопределяетfoo
метод, поэтому нет необходимости «поднимать» его видимость.
Этот метод соединения, по-видимому, обусловленэта ошибка(исправлено в Java 6). Это означает, что такие методы моста не будут генерироваться до Java 6, тогдаC#foo
Невозможно использовать вызовы отражения вне своего пакета, поэтому следующий код будет сообщать, когда версия Java меньше 1.6.IllegalAccessException
аномальный.
package samplefive;
...
SampleFour.C.class.getMethod("foo").invoke(new SampleFour.C());
...
Без использования механизма отражения будут работать обычные вызовы.
Могут быть и другие случаи, когда используется метод сопряжения, но соответствующие источники информации отсутствуют. Кроме того, нет четкого определения методов соединения, хотя вы можете легко догадаться, поскольку приведенный выше пример довольно очевиден, было бы лучше, если бы была какая-то спецификация, которая делала бы методы соединения ясными. Хотя с Java 5Method#isBridge()
методЭто общедоступный API отражения, и флаг моста такжеФормат файла байт-кода, но ни в виртуальной машине Java, ни в спецификации языка Java нет точной документации по методам моста, а также они не предоставляют никаких правил о том, когда и как компилятор использует методы моста. Все, что я могу найти, находится в"Обсуждение" здесьцитаты.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллекти другие поля, если вы хотите видеть больше качественных переводов, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.