Циклическое наследование интерфейсов

Я понимаю, почему циклическое наследование классов не разрешено в Java, но я не понял, почему циклическое наследование интерфейсов не разрешено. Проиллюстрировать:

interface Foo extends Bar {/*methods and constants*/}

interface Bar extends Foo {/*methods and constants*/}

Интерфейсы не нуждаются в инстанцировании, тогда что мешает им расширять друг друга?

Кстати, я читал этот вопрос, но речь идет не об интерфейсах, а о классах: Иерархия циклического наследования в Java

Заранее спасибо.


person Haggra    schedule 08.02.2016    source источник
comment
Что хорошего может выйти из такого разрешения?   -  person Tunaki    schedule 08.02.2016
comment
...не могли бы вы просто полностью объединить их вместе или иметь общую базу, если хотите?   -  person EpicPandaForce    schedule 08.02.2016
comment
Могут быть очень редкие случаи, когда каждый интерфейс соответствует правилу is-a, поэтому может расширять друг друга. Это не так. Почему это не разрешено? Представляет ли это какую-либо опасность для программы? Не потому, что я собираюсь использовать это наверняка, а потому, что я могу чему-то научиться из этого.   -  person Haggra    schedule 08.02.2016
comment
суперкласс должен быть классом   -  person Greesh Kumar    schedule 08.02.2016
comment
Если Foo extends Bar, то каждый экземпляр Foo также является Bar. Если Bar extends Foo, то каждый экземпляр Bar также является Foo. Если бы оба условия были истинными, то единственным способом удовлетворения этих двух условий было бы условие Foo == Bar.   -  person Andy Turner    schedule 08.02.2016


Ответы (3)


Нет, но расширение интерфейса — это способ разделения соглашения. Помните, что интерфейс — это соглашение о реализации набора методов.

public interface A extends B {
    public void myMethod();
    public void myOtherMethod();
}

Вы говорите, что интерфейс A определяется этими методами и всеми методами интерфейса B. Теперь, если интерфейс B говорит..

public interface B extends A {}

вы говорите, что интерфейс B определяется методами интерфейса A. Ну что определяет интерфейс A. Пара методов и интерфейс B. А что определяет интерфейс B? Интерфейс A, который определяется парой методов и интерфейсом B! Видите, куда это идет?

Не имеет логического смысла допускать это.

person christopher    schedule 08.02.2016

Теоретических трудностей, вероятно, нет, но это создало бы ненужные сложности. Несколько названий:

  • В настоящее время обход интерфейсов классов (через рекурсивные вызовы Class.getInterfaces()) гарантированно дает конечный результат, возможно, с повторениями, но тем не менее. Например, такой код действителен:

    private static void fillInterfaces(Class<?> clazz, Set<Class<?>> set) {
        if(clazz == null) return;
        for (Class<?> iclass : clazz.getInterfaces()) {
            set.add(iclass);
            fillInterfaces(iclass, set);
        }
        fillInterfaces(clazz.getSuperclass(), set);
    }
    
    public static Set<Class<?>> getAllInterfaces(Class<?> clazz) {
        Set<Class<?>> result = new HashSet<>();
        fillInterfaces(clazz, result);
        return result;
    }
    

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

  • В настоящее время (в Java-8) интерфейс может также определять реализацию по умолчанию для своего родительского интерфейса, при необходимости заменяя родительскую реализацию. Например:

    interface A {
        default public String getX() {return "A";}
    }
    
    interface B extends A {
        default public String getX() {return "B";}
    }
    
    static class C implements A, B {} // ok, C.getX() returns "B"
    

    Если сейчас A extends B, то A выигрывает:

    interface A extends B {
        default public String getX() {return "A";}
    }
    
    interface B {
        default public String getX() {return "B";}
    }
    
    static class C implements A, B {} // ok, C.getX() returns "A"
    

    А если и A extends B, и B extends A? Кто победит? Что new C().getX() напечатает? Или это должен быть новый тип ошибки компиляции?

В целом кажется, что такая функция принесет больше проблем, чем пользы.

person Tagir Valeev    schedule 11.02.2016
comment
Код действителен до тех пор, пока C реализует getX(). Плохой ответ. - person Pacerier; 15.05.2020

См. Спецификацию языка Java 9.1. 3 Суперинтерфейсы и субинтерфейсы:

Интерфейс I зависит от ссылочного типа T, если выполняется любое из следующих условий:

  • Я напрямую зависит от Т.

  • I напрямую зависит от класса C, который зависит от T (§8.1.5).

  • I напрямую зависит от интерфейса J, который зависит от T (рекурсивно используя это определение).

Это ошибка времени компиляции, если интерфейс зависит от самого себя.

Если циклически объявленные интерфейсы обнаруживаются во время выполнения, когда интерфейсы загружаются, выдается ошибка ClassCircularityError (§12.2.1).

Что касается почему, мне нравится комментарий Энди Тернера:

Если Foo extends Bar, то каждый экземпляр Foo также является Bar. Если Bar extends Foo, то каждый экземпляр Bar также является Foo. Если бы оба условия были истинными, то единственным способом удовлетворения этих двух условий было бы условие Foo == Bar.

person Andreas    schedule 14.05.2016