Подтвердить что ты не робот

Когда два интерфейса имеют конфликтующие типы возврата, почему один метод становится стандартным?

В Java 8, если у меня есть два интерфейса с разными (но совместимыми) типами возврата, отражение говорит мне, что один из двух методов - это метод по умолчанию, хотя я и не объявил метод по умолчанию или предоставил тело метода.

Например, возьмите следующий фрагмент кода:

package com.company;
import java.lang.reflect.Method;

interface BarInterface {}
class Bar implements BarInterface {}

interface FooInterface {
    public BarInterface getBar();
}

interface FooInterface2 extends FooInterface {
    public Bar getBar();
}

class Foo implements FooInterface2 {
    public Bar getBar(){
        throw new UnsupportedOperationException();
    }
}

public class Main {
    public static void main(String[] args) {
        for(Method m : FooInterface2.class.getMethods()){
            System.out.println(m);
        }
    }
}

Java 1.8 выводит следующий результат:

public abstract com.company.Bar com.company.FooInterface2.getBar()
public default com.company.BarInterface com.company.FooInterface2.getBar()

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

Запуск того же кода в Java 7 дает нечто менее неожиданное, хотя и все еще запутанное, учитывая, что оба метода имеют одну и ту же подпись:

public abstract com.company.Bar com.company.FooInterface2.getBar()
public abstract com.company.BarInterface com.company.FooInterface.getBar()

Java определенно не поддерживает несколько типов возврата, поэтому этот результат все еще довольно странный.


Очевидная следующая мысль: "Хорошо, возможно, это особенное поведение, которое применяется только к интерфейсам, потому что эти методы не имеют реализации".

Неправильно.

class Foo2 implements FooInterface2 {
    public Bar getBar(){
        throw new UnsupportedOperationException();
    }
}

public class Main {
    public static void main(String[] args) {
        for(Method m : Foo2.class.getMethods()){
            System.out.println(m);
        }
    }
}

дает

public com.company.Bar com.company.Foo2.getBar()
public com.company.BarInterface com.company.Foo2.getBar()

Что здесь происходит? Почему Java перечисляет их как отдельные методы и как один из методов интерфейса стал стандартным методом без реализации?

4b9b3361

Ответ 1

Это не метод default, который вы предоставляете, а метод моста. В родительском интерфейсе вы определили.

public BarInterface getBar();

и вы должны иметь метод, который можно вызвать, который реализует это.

например.

FooInterface fi = new Foo();
BarInterface bi = fi.getBar(); // calls BarInterface getBar()

Тем не менее, вам также нужно иметь возможность называть его ко-вариантом возвращаемого типа.

FooInterface2 fi = new Foo();
Bar bar = fi.getBar(); // calls Bar getBar()

Это один и тот же метод, разница только в том, что один вызывает другой и возвращает возвращаемое значение. Это метод, который, как представляется, имеет реализацию default, как это делается на интерфейсе, который делает это.

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

Причина этого в том, что JVM позволяет иметь несколько методов с различным типом возврата, потому что тип возврата является частью подписи. Я вызываюлю, чтобы указать, какой тип возврата он ожидает, и JVM на самом деле не понимает варианты совместного варианта возврата.