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


123

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

例如,给定接口:

public interface IAnything {
  void m1();
  void m2();
  void m3();
}

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

public abstract class AbstractThing implements IAnything {
  public void m1() {}
  public void m3() {}
}

你能解释为什么吗?


2
不能创建抽象类的对象。因此,只要不为抽象类提供实现,就不能为IAnything创建对象。因此,这对于编译器来说绝对没问题。编译器希望,任何实现IAnything的非抽象类都必须实现从IAnything声明的所有方法。而且由于必须扩展并实现AbstractThing才能创建对象,因此,如果该实现未实现AbstractThing遗漏的IAnything方法,则编译器将引发错误。
VanagaS

我有一个具体的类,它在与此相同的情况下扩展了自己的“ AbstractThing”,即使我没有在接口中实现一种方法,也无法解释。现在它正在执行我期望的操作,但是我无法弄清楚是什么导致它成功。我怀疑我没有找到:w其中一个文件。
Braden Best

你可以看到前面回答了类似的问题stackoverflow.com/questions/8026580/...
待办事项新和成VY

Answers:


155

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

按照您的示例代码,尝试在AbstractThing不实现该m2方法的情况下创建的子类,然后查看编译器给您带来的错误。它将迫使您实现此方法。


1
我认为编译器仍应针对未完全实现接口的抽象类抛出警告,这仅仅是因为您随后需要遍历2个类定义而不是1个,才能了解子类中的需求。但是,这是语言/编译器的限制。
workmad3

3
那不是一个好主意,因为通常会有很多抽象类,并且“ false”警告很快就会使您不知所措,从而导致您错过“ true”警告。如果您考虑一下,专门使用'abstract'关键字告诉编译器禁止该类的警告。
belugabob

4
@workmad-如果您对接口方法的子集具有通用的实现,则将其分解为一个单独的基类(DRY
优先

4
要求您将空方法实现放在抽象类中是很危险的。如果这样做了,那么子类的实现者将继承此非行为,而编译器不会告诉他们存在问题。
比尔蜥蜴

8
我认为工作狂可能建议您在没有方法主体的情况下在抽象类中定义方法并将其标记为抽象。对我来说似乎不是一个坏主意。
多纳尔

33

很好。
您不能实例化抽象类。但是抽象类可用于容纳m1()和m3()的常见实现。
因此,如果每个实现的m2()实现都不相同,但m1和m3则不同。您可以仅使用不同的m2实现创建不同的具体IAnything实现,并从AbstractThing派生而出-遵守DRY原则。验证接口是否完全为抽象类实现是徒劳的。

更新:有趣的是,我发现C#将其强制作为编译错误。在这种情况下,您被迫复制方法签名并在抽象基类中为它们添加“抽象公共”前缀。(每天都有新变化:)


7

没关系。要了解以上内容,您必须首先了解抽象类的性质。在这方面,它们与接口相似。这就是甲骨文在这里所说的。

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

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

//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);
}

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

希望有帮助...和阿拉胡阿拉姆!


这是一个有趣的观点。这让我认为“抽象类”实际上是“具体接口”,即具有某些具体方法的接口,而不是具有某些抽象方法的类。
朱利奥·皮安卡斯特利(Giulia Piancastelli)

两者都真的 但是可以肯定的是,它们不是可实例化的。
感谢2015年

4

接口表示一个类,该类没有实现其方法,而只有声明。
另一方面,抽象类是可以实现某些方法以及仅声明但不实现的方法的类。
当我们实现抽象类的接口时,它意味着抽象类继承了该接口的所有方法。因为,在抽象类中实现所有方法并不重要,但是涉及抽象类(也通过继承),因此抽象类可以将某些方法留在接口中,而无需在此处实现。但是,当此抽象类将由某个具体类继承时,它们必须必须在抽象类中实现所有那些未实现的方法。


4

给定接口:

public interface IAnything {
  int i;
  void m1();
  void m2();
  void m3();
}

Java实际上是这样看的:

public interface IAnything {
  public static final int i;
  public abstract void m1();
  public abstract void m2();
  public abstract void m3();
}

因此,您可以不使用其中某些(或全部)abstract方法,就像在abstract类扩展另一个abstract类的情况下一样。

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

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


使用javap IAnything.class生成的第二代码片段。
sharhp

3

当抽象类实现接口时

在“接口”部分中,指出了实现接口的类必须实现接口的所有方法。但是,可以定义一个不实现所有接口方法的类,前提是该类被声明为抽象的。例如,

abstract class X implements Y {   
    // implements all but one method of Y
}

class XX extends X {   
    // implements the remaining method in Y 
} 

在这种情况下,类X必须是抽象的,因为它不能完全实现Y,但是实际上,类XX可以实现Y。

参考:http : //docs.oracle.com/javase/tutorial/java/IandI/abstract.html


1

不需要抽象类即可实现这些方法。因此,即使实现了接口,接口的抽象方法也可以保持抽象。如果您尝试在具体类中实现接口(即非抽象)并且未实现抽象方法,则编译器将告诉您:要么实现抽象方法,要么将类声明为抽象。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.