2008-10-13 82 views
99

当您使用抽象类来实现接口时,Java中会出现一个奇怪的事情:接口的某些方法可能完全缺失(即,既不存在抽象声明也不存在实际实现),但编译器不会抱怨。为什么实现接口的抽象类可能会错过接口方法之一的声明/实现?

例如,给出的接口:

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

以下抽象类没有警告或错误被欢快编译:

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

你能解释一下为什么吗?

+0

不能创建抽象类的对象。所以,只要没有为抽象类提供实现,就不能为IAnything创建对象。所以这对于编译器来说绝对好。编译器期望,任何实现IAnything的非抽象类都必须实现所有由IAnything声明的方法。由于必须扩展和实现AbstractThing才能创建对象,因此编译器会抛出一个错误,如果该实现没有实现AbstractThing遗漏的IAny方法。 – VanagaS 2017-03-19 06:25:36

+0

我们可以称之为“适配器设计模式”吗?如果我们在抽象类中保留m1,m2,m3空体。 然后MyMainClass扩展抽象类&我可以自由地覆盖任何我想要的方法,没有强制覆盖所有方法。 请指教! – 2018-01-06 20:04:18

回答

130

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

按照您的示例代码,尝试制作AbstractThing的子类,但不执行m2方法,并查看编译器给您提供的错误。它会迫使你实施这种方法。

+0

我认为编译器应该仍然会针对不完整地实现接口的抽象类发出警告,只是因为您需要查看2个类定义而不是1个来查看子类中需要的内容。尽管这是一种语言/编译器限制。 – workmad3 2008-10-13 15:11:25

+3

这不会是一个好主意,因为通常会有很多抽象类,而'错误'警告很快就会压倒你,导致你错过了'真正'的警告。 如果你仔细想一想,那么'abstract'关键字就是专门告诉编译器禁止该类的警告。 – belugabob 2008-10-13 15:16:25

30

完美无瑕。
你不能实例化抽象类..但抽象类可以用来容纳m1()和m3()的通用实现。
所以如果 m2()对于每个实现的实现是不同的,但是m1和m3不是。您可以使用不同的m2实现创建不同的具体IAnything实现,并从AbstractThing派生 - 遵循DRY原则。验证接口是否完全实现为抽象类是徒劳的..

更新:有趣的是,我发现C#强制执行这个作为编译错误。您必须复制方法签名并将其作为抽象基类中的“抽象公共”前缀。在这种情况下,抽象基类中会有一些新东西。(一些新的东西每天:)

1

抽象类不需要实现这些方法。所以即使它实现了一个接口,接口的抽象方法仍可以保持抽象。如果你试图在一个具体的类中实现一个接口(即不是抽象的),并且你没有实现抽象方法,那么编译器会告诉你:实现抽象方法或者把类声明为抽象类。

2

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

5

这很好。为了理解上述内容,你必须首先理解抽象类的本质。它们在这方面与接口相似。这就是Oracle对这个here所说的话。

Abstract classes are similar to interfaces. You cannot instantiate them, and they may contain a mix of methods declared with or without an implementation.

所以你必须考虑当一个接口扩展另一个接口时会发生什么。例如...

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

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

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

2

When an Abstract Class Implements an Interface

In the section on Interfaces, it was noted that a class that implements an interface must implement all of the interface's methods. It is possible, however, to define a class that does not implement all of the interface's methods, provided that the class is declared to be abstract. For example,

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

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

In this case, class X must be abstract because it does not fully implement Y, but class XX does, in fact, implement Y.

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

1

鉴于接口:

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

这是豪W¯¯Java的实际看到它:

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

所以,你可以离开的这些abstract方法的一些(或全部)未实现的,就像你在延伸的另一abstractabstract类的情况下做的。

当你implementinterface,所有interface方法必须在派生class实施的规则,只适用于具体的实施class(即,这是不abstract本身)。

如果你确实在创造一个abstract class出来的计划,那么就没有规定说你已经到implement所有interface方法(注意,在这种情况下,它是强制性的声明派生classabstract

相关问题