【问题标题】:Cyclic Inheritance Of Interfaces接口的循环继承
【发布时间】:2016-05-18 03:31:05
【问题描述】:

我明白为什么 Java 中不允许类的循环继承,但我不明白为什么不允许接口的循环继承。举例说明:

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

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

接口不需要实例化,那么是什么阻止了它们相互扩展呢?

顺便说一句,我读了这个问题,但这不是关于接口而是类: Cyclic inheritance hierarchy in Java

提前致谢。

【问题讨论】:

  • 允许这样做有什么好处?
  • ...如果你愿意的话,你不能把它们完全合并在一起或者有一个共同的基础吗?
  • 在极少数情况下,每个接口都适合“is-a”规则,因此可以相互扩展。事实并非如此。为什么不允许这样做?这对编程有任何危险吗?不是因为我肯定会使用它,而是因为我可以从中学到一些东西。
  • 超类必须是类
  • 如果Foo extends Bar,那么Foo的每个实例也是Bar。如果Bar extends Foo,那么Bar 的每个实例也是Foo。如果两者都被允许为真,那么满足这两个条件的唯一方法是Foo == Bar

标签: java inheritance interface cyclic


【解决方案1】:

请参阅 Java 语言规范9.1.3 Superinterfaces and Subinterfaces

如果满足以下任一条件,则接口 I 依赖于引用类型 T:

  • 我直接依赖于T。

  • 我直接依赖于一个依赖于 T 的类 C (§8.1.5)。

  • I 直接依赖于依赖于 T 的接口 J(递归使用此定义)。

如果接口依赖于自身,则会出现编译时错误。

如果在运行时检测到循环声明的接口,则在加载接口时,会抛出 ClassCircularityError (§12.2.1)。

至于为什么,我喜欢Andy Turner's comment

如果Foo extends Bar,则Foo 的每个实例也是Bar。如果Bar extends Foo,那么Bar 的每个实例也是Foo。如果两者都被允许为真,那么满足这两个条件的唯一方法就是Foo == Bar

【讨论】:

    【解决方案2】:

    可能没有理论上的困难,但这会造成不必要的复杂性。举几个例子:

    • 当前遍历类接口(通过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 BB extends A 都存在呢?谁会赢? new C().getX() 会打印什么?还是应该是新类型的编译错误?

    总的来说,这样的功能似乎带来的问题多于产生的好处。

    【讨论】:

    • 只要 C 实现了 getX(),代码就有效。错误的答案。
    【解决方案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定义!看看这是怎么回事?

    允许这样做没有逻辑意义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-10
      • 2014-07-11
      • 2010-09-21
      • 2015-10-12
      相关资源
      最近更新 更多