我确切地知道接口和抽象类之间有什么区别,但为什么接口有用呢?看这个 :
现在想象抽象Point
和Circle
。你如何实现MovablePoint
既是Movable
又是Point
?只有接口可以为您提供,这就是它们的用途。
见HERE
- 如果您认为计划使用继承,那么抽象类很好,因为它为派生类提供了一个公共基类实现。
- 如果您希望能够声明非公共成员,那么抽象类也很好。在界面中,所有方法都必须是公共的。
- 如果您认为将来需要添加方法,那么抽象类是更好的选择。因为如果向接口添加新的方法标题,则必须更改已实现该接口的所有类以实现新方法。这可能很麻烦。
- 当您认为API暂时不会发生变化时,接口是一个不错的选择。
- 当你想要有类似于多重继承的东西时,接口也很好,因为你可以实现多个接口。
因此,在您的场景中,如果MovablePoint同时是Movable和Point,则可以指定接口。
是的 - 在这种情况下你可以拥有,但也试着看大局。当我第一次学习OOP时,我问同样的问题,并且界面让我困惑了很长时间。
如果你想将'可移动'方法添加到一个不是Point子类的对象,让我们说'MovableJPEG'或者其他类似的东西。移动操作的最终结果将是相同的,但您必须重写两个类和不同方法的接口,以处理在与Movable对象交互的类中移动这些功能。
使用接口,您只需通过具有相同方法的类似接口来传递任意数量的类型,因为它们的实现细节保证相同。
接口和抽象类都允许程序员编写模块化类。
接口相对于抽象类的优点是它不带任何预定义的方法或属性。抽象类可能包含您不希望在类中实现的内容。
第二个优点是java类只能扩展一个类,但只能扩展大量接口。
接口提供了更多的自由,抽象类可以影响类的内部设计。抽象类的一个优点是代码共享,这对于接口来说更加困难。
想象一下,使用你的图书馆的人想要介绍其他可用的东西,比如MovableTirangle
。如果他们让这个实现Movable
,它可以完美地与你的库一起使用。
例如,库提供了一个
void move(Movable m, int horiz, int vert) {
int i;
if (horiz >= 0) {
for (i=0; i < horiz; i++) {
m.moveRight();
}
} else {
for (i=0; i > horiz; i--) {
m.moveLeft();
}
}
if (vert >= 0) {
for (i=0; i < vert; i++) {
m.moveUp();
}
} else {
for (i=0; i > vert; i--) {
m.moveDown();
}
}
}
它可以用于所有当前和未来的Movable
s。
到目前为止,这对基类也是有效的,所以这并不重要。
但是,由于Java不支持多重继承,因此类不能从多个基类继承。但是如果需要的话,它可以实现多个接口。
此外,如果你有一个功能界面(你没有,因为你有多个非默认函数),你还可以使用Java的新lambda特性。这是另一个不适用于抽象类的东西。
让你试图给一些不相关的类提供类似的属性。然后你可以使用界面。例如 -
<Bounceable>
/ \
Ball Tire
这里的Ball
和Tire
(汽车)完全不相关。但他们都是Bounceable
。如果您希望两个不相关的类具有相同的属性,则可以使用interface。
接口的另一个重要用途是 - 提供多重继承的风格,但比多重继承更有效(存在常见的Deadly Diamond of Death问题)。例如,你期待Ball
应该是Bouncable
和Serializeable
。在这里,Bouncable
和Serializeable
彼此完全无关。然后你可以在这里使用界面。抽象类需要扩展/继承,而在java
中,多个inheritance
是不可能的。因此我们可以通过使用接口为类提供完全不相关的属性。
我不会列出使用接口或抽象类之间的所有差异,或者何时使用它们中的每一个,我认为你会在网上找到很多资源,所以只讨论它,例如:How should I have explained the difference between an Interface and an Abstract class?
要回答您,是的,您只能在示例中使用抽象类,而不必使用接口
但是,存在概念上的差异,没有创建接口来暴露公共行为,它是一个类可以做什么的契约。而抽象类是层次结构的父级,用于生成具有核心结构并提供默认行为的子级。
从概念上讲,Movable
必须是Interface
,因为它定义了implements Movable
可以做什么类(可以向上移动,向下移动,移动......)而不是如何做(圆圈不像Rectangle那样移动)。虽然你的MovableCircle
可能是一个abstract class
,因为我们可以定义类似的方法:calculateArea()
,getRadius()
,calculateCircumference()
,...这是从MovableWheel
继承的类的默认行为。
.
IMO虽然在人们将接口解释为合同(即实现方法签名的义务)时是正确的,但我发现他们经常忘记提及使用接口作为实现相同接口的整个对象组的类型,我相信成为难题的重要部分,了解界面的有用性。
下面是一个使用Cat和Dog类的代码示例(C#),它使用interface和absctract类,希望它们之间的差异很大。
假设1:两只动物都说声音,但这些是不同的声音(需要不同的方法)假设2:两只动物都可以吃,如果它们不满(这两种动物都需要一种方法)
static void Main(string[] args)
{
IanimalBehavior pluto = new Dog();
IanimalBehavior simba = new Cat();
Program.makeAnimals_say_and_eat(pluto);
Program.makeAnimals_say_and_eat(simba);
Program.makeAnimals_say_and_eat(pluto);
Program.makeAnimals_say_and_eat(simba);
Console.ReadLine();
}
static void makeAnimals_say_and_eat(IanimalBehavior animalObject)
{
Console.WriteLine(animalObject.makeSound());
Console.WriteLine(animalObject.eat());
}
interface IanimalBehavior {
string makeSound();
string eat();
}
class Dog : Animal, IanimalBehavior {
public string makeSound() {
return this.GetType().Name + " says: wuf";
}
}
class Cat : Animal, IanimalBehavior {
public string makeSound()
{
return this.GetType().Name + " says: miauw";
}
}
abstract class Animal {
bool _isFull = false;
public string eat()
{
if (_isFull == false)
{
_isFull = true;
return this.GetType().Name + " is now eating";
}
else
{
return this.GetType().Name + " is now too full to eat!";
}
}
}
请注意,动物被声明为接口类型:
IanimalBehavior pluto = new Dog();
这将确保方法makeAnimals_say_and_eat()可以采用针对两种类型的对象(Cat和Dog)的参数类型,因此所有动物只需要一种方法,这就是我们想要的。
static void makeAnimals_say_and_eat(IanimalBehavior animalObject)
{
Console.WriteLine(animalObject.makeSound());
Console.WriteLine(animalObject.eat());
}
该方法从作为参数传递的任何对象调用.makeSound()和.eat()。编译器很高兴,因为它知道任何IanimalBehavior类型都必须包含这两种方法,因为它在合同中这样说:
interface IanimalBehavior {
string makeSound();
string eat();
}
在.makeSound()上,返回值取决于类类型,而.eat()对于两个类都是相同的,因为eat()是在所有动物继承的absctract类Animal中声明的。
这些说明的输出:
Program.makeAnimals_say_and_eat(pluto);
Program.makeAnimals_say_and_eat(simba);
Program.makeAnimals_say_and_eat(pluto);
Program.makeAnimals_say_and_eat(simba);
是:
Dog says: wuf
Dog is now eating
Cat says: miauw
Cat is now eating
Dog says: wuf
Dog is now too full to eat!
Cat says: miauw
Cat is now too full to eat!
接口类型还允许您在单个数组中存储具有类似性质的不同对象(相同的接口实现),然后您可以迭代这些对象。
IanimalBehavior[] animal_list = { new Dog(), new Cat()};
foreach (IanimalBehavior animal in animal_list)
{
Console.WriteLine(animal.eat());
Console.WriteLine(animal.makeSound());
}