假设我们有两个类Tiger
和Aeroplane
。
这两种类型的一个共同点是速度。我知道创建超类ClassWithSpeed
然后从中派生子类Aeroplane
和Tiger
是不合逻辑的。
相反,最好创建一个包含方法speed()
的接口,然后在Aeroplane
和Tiger
中实现它。我明白了。但是,如果没有接口,我们可以做同样的事情。我们可以在speed()
中定义方法Aeroplane
,在speed()
中定义方法Tiger
。
唯一(也许是非常大的)缺陷是我们无法通过接口引用“到达”Tiger
和Aeroplane
的对象。
我是Java和OOP的初学者,如果有人向我解释接口的作用,我将非常感激。 干杯!
答案 0 :(得分:70)
这是:
public int howFast(Airplane airplane) {
return airplane.speed();
}
public int howFast(Tiger tiger) {
return tiger.speed();
}
public int howFast(Rocket rocket) {
return rocket.speed();
}
public int howFast(ThingThatHasntBeenInventedYet thingx) {
// wait... what? HOW IS THIS POSSIBLE?!
return thingx.speed();
}
...
VS
public int howFast(Mover mover) {
return mover.speed();
}
现在,想象一下将来必须返回并更改所有howFast
功能。
接口或否,每个类都必须实现或继承speed()
方法。界面允许您更轻松地使用这些不同的类,因为它们每个都承诺以某种方式运行。您将调用speed()
,他们将返回int
,因此您不必为每个可能的类编写单独的方法。
当您发现由于相对论物理学的突破而需要以不同的方式处理速度时,您只需要更新调用speed()
的方法。当你的曾孙女为HyperIntelligentMonkeyFish
写一个班级时,她不必拆解你的古代二进制文件并进行修改,以便你的代码可以监视和控制她的突变军队。她只需要声明HyperIntelligentMonkeyFish implements Mover
。
答案 1 :(得分:8)
接口允许Java,因为它是静态类型的,可以解决多种继承限制,达到语言设计者认为值得的程度。
在动态语言(如Python,Ruby等)中,您可以在任意对象上调用speed方法,并在运行时发现它是否存在。
另一方面,Java是静态类型的,这意味着编译器必须同意该方法将提前存在。在不共享共同祖先的类上执行此操作的唯一方法是,可能只有Object,这是一个接口。由于对象可以实现任意数量的接口,这意味着您可以获得多重继承的优点(可以为不同方法构建多个不同对象的单个对象),而没有缺点(两个超类中的冲突实现)
使用Java 8,该语言的设计者得出结论,没有任何实现的接口过于严格(他们不喜欢my solution我猜;-)),因此它们允许默认实现,从而扩展了接口的多继承选项,同时仍试图通过a complex set of precedence rules避免冲突的实现问题,以便有一个明确的默认实现来执行。
答案 2 :(得分:6)
我试图通过以下示例解释界面的优势 -
假设您有另一个类,您需要使用老虎或飞机作为参数。因此,通过使用界面,您可以使用
进行调用someObject.someMethod(ClassWithSpeed)
但如果你不使用界面,你可以使用
someObject.someMethod(Tiger)
someObject.someMethod(AeroPlane)
现在你该怎么办?你可能的答案就像,"I will use two overloaded method".
到目前为止这没关系,但是
假设您需要添加更多选项(例如汽车,骑行,兔子,乌龟等...... 100个其他选项)。那么你将如何改变你现有的代码呢?你需要改变很多东西......
上面的整个例子只有一个目的。也就是说
“我们需要界面来创建更好,可重用且适当的面向对象 设计“
N.B。
答案 3 :(得分:5)
定义接口允许您定义不仅适用于Airplane和Tiger的方法,还可以定义共享相同接口的其他类。
例如,假设您的界面是IObjectWithSpeed。然后你可以定义一个这样的方法 - 在一个操作IObjectWithSpeed对象的类中。
public double calculateSecondsToTravel( IObjectWithSpeed obj, double distance ) {
return distance / obj.getSpeed();
}
接口允许我们满足open/closed principle - 打开扩展,但关闭以进行修改。上面的方法的单个实现不需要修改,因为定义了实现IObjectWithSpeed的新类。
答案 4 :(得分:3)
除了描述这些类的功能之外,可以使用接口的方法,而不需要了解实现它的类,即使对于尚未定义的类也是如此。
例如,如果你需要一个计算有效路由的类Transport
,可以给它一个实现ClassWithSpeed
作为参数的类并使用它的方法speed()
用于计算它需要的东西。通过这种方式,您可以将它与我们的类Aeroplane
一起使用,也可以与我们稍后定义的任何类一起使用,比如说Boat
。 Java会注意,如果你想使用一个类作为Transport
的参数,它将实现ClassWithSpeed
,并且任何实现ClassWithSpeed
的类都会实现方法speed()
,以便它可以使用。
答案 5 :(得分:3)
我想了解很多理论细节,但会尝试用这个例子来解释。
考虑 JDBC API 。它是用于处理Java中与数据库相关的选项的API。现在,业界有这么多的数据库。如何为此编写驱动程序?好吧,快速而肮脏的方法可能是使用我们自己的类和API编写自己的实现。
但从程序员的角度思考。他们会在使用不同的数据库时开始学习 DATABASE DRIVER 的API吗?答案是否定的。
那么问题的解决方案是什么?只需拥有一个定义良好的API,任何人都可以为自己的实现进行扩展。
在JDBC API中,有一些接口是Connection,ResultSet,PreparedStatement,Statement等。现在每个数据库供应商都将实现该接口并为此编写自己的实现。结果? :减少开发人员的工作量,易于理解。
现在,这个自定义实现可能包含哪些内容?这很简单。他们做了什么,以ResultSet接口为例并实现它,并以ResultSet获取的任何方法返回,返回类的实现这样的ResultSet接口
ResultSet rs = new ResultSetImpl(); //这就是他们在内部所做的事情。
所以界面就像合同。它们定义了您的课程能够做什么,并为您的应用程序提供灵活性。您可以使用正确的接口创建自己的API。
希望这会对你有所帮助。
答案 6 :(得分:3)
接口不仅仅是方法签名。
这是一种代表合同的类型。合同是事物,而不是方法签名。当一个类实现一个接口时,这是因为有一个共享行为的契约,该类有兴趣作为一个类型实现。该合同是通过指定的成员实现的,这些成员通常是方法主体,但也可能包含静态最终字段。
老虎和飞机都可能被表达为具有通过speed()实现的共同行为的类型......如果是这样,那么界面代表表达该行为的合同。
答案 7 :(得分:1)
编译器使用接口(作为语言构造)来证明方法调用是有效的,并允许您使依赖类与实现类交互,同时尽可能少地了解实现类。
答案 8 :(得分:1)
这是一个非常广泛的问题,可以给出一个简单的答案。我可以推荐一本书Interface Oriented Design: With Patterns。它解释了接口的所有功能。为什么我们不应该避免它们。
答案 9 :(得分:1)
在语言层面,如你所提到的,接口的唯一用途是能够以通用方式引用不同的类。然而,在人员层面上,情况看起来不同:IMO Java在大型项目中使用时非常强大,其中设计和实施由不同的人完成,通常来自不同的公司。现在,系统架构师可以创建一堆类,然后实现者可以直接在他们的IDE中插入并开始处理它们,而不是在Word文档上编写规范。 换句话说,它更方便,而不是声明“类X实现方法Y和Z以使其用于目的A,只是说”类X实现接口A“
答案 10 :(得分:1)
你是否尝试过使用组合?如果我想要2个不同的类来继承相同的能力,我会使用一个类,它通过使用抽象类和检查实例来获取它所使用的类型的对象。接口可用于强制方法包含在类中,但不需要任何实现,也不适用于2组编码器在不同的编码区域工作。
Class Tiger {
public MovingEntity mover;
public Tiger(){
mover.speed=30;
mover.directionX=-1;
mover.move(mover);
}
}
Class Plane {
public MovingEntity mover;
public Plane(){
mover.speed=500;
mover.directionX=-1;
mover.move(mover);
}
Abstract Class Moverable(){
private int xPos;
private int yPos;
private int directionX;
private int directionY;
private int speed;
Class MovingEntity extends Moverable {
public void move(Moverable m){
if(m instanceof Tiger){
xPos+=directionX*speed;
yPos+=directionY*speed;
}else if(m instanceof Plane){
xPos+=directionX*speed;
yPos+=directionY*speed;
}
}
答案 11 :(得分:0)
因为创建界面会为所有这些类提供Polymorphism,即Tiger和Airplane。
当基类的功能将成为子类功能的核心时,您可以从(抽象的或具体的)类扩展。
除了核心功能之外,还希望在类中添加增强功能时使用接口。所以使用接口会给你多态性,即使它不是你的类的核心功能(因为你通过实现接口输入了合同)。与为每个类创建speed()方法相比,这是一个巨大的优势。