为什么实现接口的抽象类可能会错过接口方法之一的声明/实现?

Giu*_*lli 119 java abstract-class interface

当您使用抽象类来实现接口时,Java中会发生奇怪的事情:某些接口的方法可能完全丢失(即既不存在抽象声明也不存在实际实现),但编译器不会抱怨.

例如,给定接口:

public interface IAnything {
  void m1();
  void m2();
  void m3();
}
Run Code Online (Sandbox Code Playgroud)

以下抽象类在没有警告或错误的情况下快速编译:

public abstract class AbstractThing implements IAnything {
  public void m1() {}
  public void m3() {}
}
Run Code Online (Sandbox Code Playgroud)

你能解释一下原因吗?

Bil*_*ard 151

那是因为如果一个类是抽象的,那么根据定义,你需要创建它的子类来实例化.子类将(由编译器)需要实现抽象类遗漏的任何接口方法.

按照您的示例代码,尝试在AbstractThing不实现m2方法的情况下创建子类,并查看编译器为您提供的错误.它会强制您实现此方法.

  • 我认为workmad可能建议你在没有方法体的情况下在抽象类中定义方法并将它们标记为抽象.对我来说似乎不是一个坏主意. (7认同)
  • @workmad - 如果你有一个接口方法子集的通用实现,那么将它分解为一个单独的基类更有意义(DRY胜过一个地方的代码) (4认同)
  • *要求*将空方法实现放在抽象类中是危险的.如果你这样做,那么子类的实现者将继承这种非行为,而编译器不会告诉他们存在问题. (4认同)
  • 这不是一个好主意,因为通常会有很多抽象类,"错误"警告会很快压倒你,导致你错过'真正的'警告.如果你考虑一下,'abstract'关键字专门用于告诉编译器压制该类的警告. (3认同)
  • 我认为编译器仍然应该对未完全实现接口的抽象类发出警告,因为您需要查看 2 个类定义而不是 1 个类定义,以了解您在子类中需要什么。不过,这是语言/编译器限制。 (2认同)

Gis*_*shu 33

非常好.
您无法实例化抽象类..但是抽象类可用于容纳m1()和m3()的常见实现.
因此,如果每个实现的m2()实现不同,但m1和m3不是.您可以使用不同的m2实现创建不同的具体IAnything实现,并从AbstractThing派生 - 尊重DRY原则.验证接口是否完全为抽象类实现是徒劳的..

更新:有趣的是,我发现C#强制执行此编译错误.你被迫复制方法签名,并在这个场景中用抽象基类中的'abstract public'作为前缀..(每天都有新的东西:)


Gra*_*ful 7

没关系.要理解上述内容,您必须首先了解抽象类的本质.它们在这方面类似于接口.这就是Oracle 在这里所说的.

抽象类与接口类似.您无法实例化它们,并且它们可能包含使用或不使用实现声明的混合方法.

因此,您必须考虑当接口扩展另一个接口时会发生什么.例如 ...

//Filename: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

//Filename: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}
Run Code Online (Sandbox Code Playgroud)

......正如你所看到的,这也很好地编译.仅仅因为,就像抽象类一样,接口无法实例化.因此,不需要从其"父"明确提及方法.但是,所有父方法签名都隐式地成为扩展接口或实现抽象类的一部分.因此,一旦适当的类(可以实例化的类)扩展了上述内容,就需要确保实现每个抽象方法.

希望有助于......和Allahu'阿拉姆!


sha*_*rhp 6

给定接口:

public interface IAnything {
  int i;
  void m1();
  void m2();
  void m3();
}
Run Code Online (Sandbox Code Playgroud)

Java 实际是这样看待它的:

public interface IAnything {
  public static final int i;
  public abstract void m1();
  public abstract void m2();
  public abstract void m3();
}
Run Code Online (Sandbox Code Playgroud)

因此,您可以保留其中一些(或全部)abstract方法未实现,就像在abstract类扩展另一个abstract类的情况下所做的那样。

当您implement使用 时,所有方法必须在派生中实现的interface规则仅适用于具体实现(即不是其本身)。interfaceclassclassabstract

如果您确实计划创建一个abstract class,那么没有规则说您必须使用implement所有interface方法(请注意,在这种情况下,必须将派生声明classabstract