[Перевод] Метод моста Java в деталях

Java задняя часть переводчик Программа перевода самородков

Метод моста в 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,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллекти другие поля, если вы хотите видеть больше качественных переводов, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.