为什么接口有用?

Why are interfaces helpful?

我完全知道接口和抽象之间的区别 类,但为什么接口有用?看看这个:

想象一下,有人在使用您的图书馆,想介绍一些其他可用的东西,例如 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();
        }
    }
}

可用于所有当前和未来的 Movables。

到现在为止,这对基本 classes 也有效,所以这不算数。

但是,由于 Java 不支持多重继承,一个 class 不能从多个基础 class 继承。但如果需要的话,它可以实现多个接口。

此外,如果您有函数式接口(您没有,因为其中有多个非默认函数),您还可以使用 Java 的新 lambda 功能.这是另一件不适用于抽象 classes.

的事情

现在想象抽象 PointCircle。你如何实现 MovablePoint 既是 Movable 又是 Point?只有界面可以为您提供这些,这就是它们存在的目的。

是的——在这种情况下你可以,但也试着放眼大局。刚开始学OOP的时候也问过同样的问题,接口让我困惑了很久。

如果您想将 'movable' 方法添加到不是 Point 的子类的对象,比如 'MovableJPEG' 或类似的对象,该怎么办?移动操作的最终结果是相同的,但您必须重写 类 和不同方法的接口,以处理在与可移动对象交互的 类 中移动这些功能。

使用接口,您可以传递任意数量的相关类型,因为它们的实现细节保证相同。

接口和抽象 classes 都允许程序员编写模块化 classes。

接口相对于抽象 class 的优势在于它不带有任何预定义的方法或属性。摘要 class 可能包含您不希望在 class 中实现的内容。

第二个优点是一个javaclass只能扩展一个class,但是可以扩展很多接口

接口提供了更多的自由,抽象 class 可以影响 class 的内部设计。抽象 class 的一个优点是代码共享,这对于接口来说更加困难。

  1. 您在 java 中没有多重继承。所以多个classes不能在同一个class中继承,但是可以实现多个接口
  2. 有助于使事情井井有条。就像所有与 DOG 相关的东西都在一个界面下,所有 CAT 都在 CAT 下等等。
  3. 运行时多态性:通过接口你可以有超class引用变量引用不同的子classes。这有助于保持代码清洁,提高可伸缩性(使所有那些 bridge/proxy/factory 等设计模式成为可能,否则这些设计模式可能不存在)。

HERE

  • An abstract class is good if you think you will plan on using inheritance since it provides a common base class implementation to derived classes.
  • An abstract class is also good if you want to be able to declare non-public members. In an interface, all methods must be public.
  • If you think you will need to add methods in the future, then an abstract class is a better choice. Because if you add new method headings to an interface, then all of the classes that already implement that interface will have to be changed to implement the new methods. That can be quite a hassle.
  • Interfaces are a good choice when you think that the API will not change for a while.
  • Interfaces are also good when you want to have something similar to multiple inheritance, since you can implement multiple interfaces.

所以在您的场景中,您只能通过接口指定 MovablePoint 是否既是 Movable 又是 Point。

概念差异:

我不会列出使用接口或抽象 classes 或何时使用它们之间的所有差异,我想您会在网络上找到很多资源,所以只讨论这些,例如:How should I have explained the difference between an Interface and an Abstract class?

回答你,,你可以在你的例子中只使用抽象class,而不必使用接口

但是,有一个概念上的差异,没有创建接口来公开public行为,它是一个契约class 能做什么。 虽然抽象 classes 是层次结构的父级,以生成具有核心结构并提供默认行为的子级。

类比你的例子:

从概念上讲,Movable 必须是一个 Interface,因为它定义了 class implements Movable 可以做什么 (可以向上移动,向下移动,移动...) 而不是 如何 去做(圆形不像矩形那样移动)。虽然您的 MovableCircle 可能是 abstract class,因为我们可以定义如下方法:calculateArea()getRadius()calculateCircumference(),...这是 [的默认行为=46=]es 将像 MovableWheel.

一样从它继承

.

让您尝试将一组相似的 属性 提供给一些不相关的 class。然后你可以使用接口。例如 -

       <Bounceable>
        /     \
      Ball    Tire 

这里BallTire(汽车的)完全没有关系。但是他们两个都是Bounceable。如果你想让两个不相关的 class 有相同的 属性 那么你可以使用接口。

接口还有另一个重要用途 - 提供多重继承的风格,但比多重继承更有效(这里有一个常见的 Deadly Diamond of Death 问题。)。例如,您期望 Ball 应该是 BouncableSerializeable。这里 BouncableSerializeable 彼此完全无关。然后你可以在这里使用接口。摘要 class 需要 extended/inherited 而在 java 中多个 inheritance 是不可能的。所以我们可以通过使用 interface.

向 class 提供完全不相关的 属性

IMO 虽然人们将接口解释为契约是正确的,即实现方法签名的义务,但我发现他们经常忘记提及使用接口作为实现同一接口的整个对象组的类型,我相信这是拼图的重要部分,了解界面的实用性。

这是一个使用 Cat 和 Dog classes 的代码示例 (C#),它同时使用接口和抽象 class,希望能突出它们之间的差异。

假设 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() 可以采用针对两种类型对象(猫和狗)的参数类型,因此所有动物只需要一个方法,这正是我们想要的。

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() 上 return 值取决于 class 类型,而 .eat() 对于两个 classes 是相同的,因为 eat() 是在抽象中声明的class 所有动物都继承自的动物。

这些指令的输出:

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