【问题标题】:Is there a way to guarantee an interface extends a class in Java?有没有办法保证接口扩展 Java 中的类?
【发布时间】:2013-01-01 09:02:27
【问题描述】:

假设我有以下情况:

public abstract class Vehicle {
  public void turnOn() { ... }
}

public interface Flier {
  public void fly();
}

有没有一种方法可以保证任何实现Flier 的类也必须扩展Vehicle?我不想让Flier 成为一个抽象类,因为我希望能够以类似的方式混合其他几个接口。

例如:

// I also want to guarantee any class that implements Car must also implement Vehicle
public interface Car {
  public void honk();
}

// I want the compiler to either give me an error saying
// MySpecialMachine must extend Vehicle, or implicitly make
// it a subclass of Vehicle. Either way, I want it to be
// impossible to implement Car or Flier without also being
// a subclass of Vehicle.
public class MySpecialMachine implements Car, Flier {
  public void honk() { ... }
  public void fly() { ... }
}

【问题讨论】:

  • 接口扩展接口。 (抽象)类(部分)实现接口并扩展其他类。
  • 试试我的建议,如果没有从 Vehicle 扩展,您将无法编译 MySpecialMachine!

标签: java inheritance interface


【解决方案1】:

Java 接口不能扩展类,这是有道理的,因为类包含无法在接口中指定的实现细节..

解决这个问题的正确方法是通过将Vehicle 也转换为接口来将接口与实现完全分离。 Car 等。可以扩展Vehicle接口,强制程序员实现相应的方法。如果您想在所有 Vehicle 实例之间共享代码,那么您可以使用一个(可能是抽象的)类作为任何需要实现该接口的类的父类。

【讨论】:

  • 如果他不能实现相应的方法?如果只有 Vehicle 可以实现相应的方法?想一想。
  • @PeterRader:我不明白你在说什么。他已经实现了Vehicle 中的方法。他所要做的就是将Vehicle 转换为接口,并在必要时将任何实现细节移至抽象基类...
  • @PeterRader:所以?要么Vehicle 将被转换为接口,并且它的代码移动到另一个基类中,要么Vehicle 必须实现一个新接口,比如说VehicleInterface',,然后它将被所有其他接口扩展。最终结果在技术上是相同的......
  • OP 没有询问正确的方法。 Op 询问如果类是可飞行的,如何实现从 Vehicle 扩展的胁迫。您的方式会自动表明所有车辆也必须是 Flier 的实例。好吧,我现在开车回家;D
【解决方案2】:

你可以像这样重新排列你的类和接口:

public interface IVehicle {
  public void turnOn();
}

public abstract class Vehicle implements IVehicle {
  public void turnOn() { ... }
}

public interface Flier extends IVehicle {
  public void fly();
}

这样Flier 的所有实现都可以保证实现车辆的协议,即IVehicle

【讨论】:

  • 一点点挑剔 - 如果要使用 I* 约定(顺便说一句,在我看来,它从来没有像 Java 那样)那么它应该用于 all 接口...
  • @thkala:是的,I* 约定主要是 .NET 的东西。我使用它是因为我需要与 Vehicle 不同但相关的名称。清理名称以获得一致的命名肯定会有所帮助。
【解决方案3】:

如果您可以控制Vehicle 类,只需将Vehicle 提取为接口,然后提供基本实现。

如果您无法控制 Vehicle 类,例如因为它是您正在使用的框架或第三方库的一部分,则无法在 Java 中进行。

您可以做的最接近的事情是使用泛型多通配符表示法。

<T extends Vehicle & Car>

但除非你这样做,否则你不能真正将它直接应用到 Car:

public interface Car<T extends Vehicle & Car>() {
    T self();
}

这很奇怪,并且不强制 self 方法实际返回 self,这只是一个强烈的提示/建议。

您可以像这样实现Car

public class CitroenC3 extends Vehicle implements Car<CitroenC3> {
    @Override
    public CitroenC3 self() {
        return this;
    }
}

可以像这样使用Car&lt;?&gt;

Car<?> car = obtainCarInSomeWay();
Vehicle v = car.self();
Car c = car.self();

它们都应该是有效的语法。

编译器在此强制执行的是您在Car&lt;WHICH&gt; 中指定的内容必须同时扩展Vehicle 并实现Car。通过添加self(),您是在告诉程序员T 对象应该是对象本身,因此如果他想符合规范,则强制通配符实例与类匹配。

在 Java 8 中,您甚至可以为 self 方法定义默认实现。

我也希望有更好的方法来处理这样的事情。

【讨论】:

    【解决方案4】:

    这是一个奇怪的要求,但您可以使用泛型完成类似的事情:

    <T extends MyInterface & MyAbstractClass>
    

    【讨论】:

      【解决方案5】:

      这道题说明你还没有掌握interfaceclass的精髓。现在忘记具体的 Java 语法,您首先需要了解的是:接口是一组协议,它应该与实现无关。让接口扩展一个类(这是面向实现的)是没有意义的。

      回到您的具体问题,如果您想保证Flier 始终是Vehicle 的一种,只需将后者更改为interface 并让前者扩展它(扩展一个确实有意义来自另一个协议的协议)。之后,您可以创建任何实现VehicleFlier 的类(抽象或具体)。

      【讨论】:

      • 可能是这种情况,但 Op 可能正在使用一个外部框架,其中 Vehicle 是一个类(即使它应该是一个接口)。
      【解决方案6】:
      1. 定义一个新的包
      2. 使用方法“implementMe(HiddenOne)”创建一个范围为“default”的新接口(即 HiddenOne)
      3. 将车辆和飞行器移至新包裹。
      4. 从 HiddenOne 继承载具和飞行器
      5. 在 Vehicle 中实现方法 implementMe。

      现在: 每当您想从“Flier”实现时,您必须从 Vehicle 扩展! (因为只有 Vehicle 可以实现 implementMe)。

      这很棘手,但效果很好。

      【讨论】:

      • 还有一个更复杂的解决方案,使用受保护的内部类。
      • 如果有人在这个新包中创建了一个Submarine 类怎么办?然后他们可以直接实现HiddenOne,而不需要经过Vehicle...
      • 您可以使用安全规则保护您的包裹。但是......好吧,受保护的内部类可能更容易。
      猜你喜欢
      • 1970-01-01
      • 2015-02-09
      • 1970-01-01
      • 1970-01-01
      • 2015-01-30
      • 2021-12-20
      • 1970-01-01
      • 2021-08-25
      • 1970-01-01
      相关资源
      最近更新 更多