限制Java接口的适用性

use*_*051 2 java

是否可以限制 Java 接口的适用性?换句话说,如果我有一个类 A 和一个 Java 接口 I:是否可以强制 I 的实例也是 A 的实例?

或者,换句话说:如果我有类似的东西

public class B implements I {
}
Run Code Online (Sandbox Code Playgroud)

如果 B不是A 的子类,我希望编译器抛出错误。

A是我无法修改的上游类。(我自己的)的一些子类A将实现I,但其他子类则不会。

我的界面将包含很多方法,例如:

public default void foo() { 
    ((A) this).doSomething(...); 
}
Run Code Online (Sandbox Code Playgroud)

我的目的是确保强制转换A能够正常工作。

Cha*_*ire 8

如果您使用的是 java 17+,则可以使用密封接口/类。由于A不可修改,您需要一个自己的父类。仅允许BaseForI(或其他合适的名称)实施I.

当您使用关键字密封接口(或类)时sealed,您还可以声明允许的继承者permits

public sealed interface I permits BaseForI {

  default void foo() {
    ((BaseForI) this).doSomething();
  }
}
Run Code Online (Sandbox Code Playgroud)

允许的继承者(类或另一个接口)必须声明:

  1. final- 你不能扩展它(仅适用于一个类)。
  2. non-sealed- 开放扩展。
  3. sealed- 继承人也可以被封印,规则相同。

对于您的情况,您希望父级不被密封:

public non-sealed class BaseForI extends A implements I {
}
Run Code Online (Sandbox Code Playgroud)

编译器不允许I, 的实现者不同于BaseForI, 是一个A.

public class B implements I {
}
Run Code Online (Sandbox Code Playgroud)

上面的示例会导致编译错误 - 'B' is not allowed in the sealed hierarchyB允许从BaseForI另一方面延伸,因为底为non-sealed

public class B extends BaseForI {
}
Run Code Online (Sandbox Code Playgroud)

但是有一个很大的但是- 这是一个很好的例子,为什么你应该支持组合而不是继承。关于该主题的好读物 -更喜欢组合而不是继承?。为 提供一个基本实现I,这取决于A完成这项工作。

public abstract class CompositionClass implements I {

  private final A a;

  @Override
  public void foo() {
    this.a.doSomething();
  }
}
Run Code Online (Sandbox Code Playgroud)

不需要铸造并且不太复杂。您仍然可以密封I以允许仅通过 的扩展路径CompositionClass

  • @Michael 好吧,这正是**问题想要的** - “如果 B 不是 A 的子类,我希望编译器抛出错误。” (2认同)