CodeGym /Java 博客 /随机的 /为什么我们需要 Java 中的接口
John Squirrels
第 41 级
San Francisco

为什么我们需要 Java 中的接口

已在 随机的 群组中发布
你好!今天我们要聊聊Java中的一个重要概念:接口。这个词你可能很熟悉。例如,大多数计算机程序和游戏都有界面。从广义上讲,接口是一种连接两个交互方的“遥控器”。日常生活中界面的一个简单示例是电视遥控器。它连接两个对象——一个人和一台电视——并执行不同的任务:调高或调低音量、切换频道以及打开或关闭电视。一方(人)需要访问界面(按遥控器上的按钮)才能使第二方执行操作。例如,让电视切换到下一个频道。更重要的是,用户不 不需要知道电视是如何组织的或者频道切换过程是如何在内部实现的。用户唯一可以访问的是界面。主要目标是获得所需的结果。这与编程和 Java 有什么关系?一切 :) 创建接口与创建常规类非常相似,但使用的是单词,我们指示单词interface。让我们看看最简单的 Java 接口,看看它是如何工作的,以及我们为什么需要它:

public interface CanSwim {

     public void swim();
}
我们已经创建了一个CanSwim界面。它有点像我们的遥控器,但有一个“按钮”:swim()方法。但是我们如何使用这个遥控器呢?为此,我们需要实现一个方法,即我们的遥控器按钮。要使用接口,我们程序中的一些类必须实现它的方法。让我们发明一个对象“会游泳”的类。例如,Duck类适合:

public class Duck implements CanSwim {

    public void swim() {
        System.out.println("Duck, swim!");
    }

    public static void main(String[] args) {

        Duck duck = new Duck();
        duck.swim();
    }
}
“我们在这里看到了什么?Duck类通过implements关键字与CanSwim接口‘关联’ 。您可能还记得我们使用类似的机制通过继承关联两个类,但在那种情况下我们使用了扩展这个词。对于完全清楚,我们可以将“ public class Duck implements CanSwim ”直译为:“public Duck class implements CanSwim interface”。这意味着与接口关联的类必须实现其所有方法。注意:我们的类,就像接口,有一个方法,它包含一些逻辑。这是强制要求。如果我们只写DuckCanSwimswim()public class Duck implements CanSwim没有swim()Duck类中创建方法,编译器会给我们一个错误: Duck is not abstract and does not override abstract method swim() in CanSwim 为什么?为什么会这样?如果我们用电视的例子来解释这个错误,这就像给某人一个电视遥控器,上面有一个不能改变频道的“改变频道”按钮。您可以随心所欲地按下按钮,但它不起作用。遥控器本身不换台:它只是向电视发送一个信号,电视实现复杂的换台过程。我们的鸭子也是如此:它必须知道如何游泳才能使用CanSwim界面调用它。如果它不知道如何,CanSwim接口不连接两方——人和程序。此人将无法使用该方法在程序内游泳swim()Duck现在你更清楚接口是做什么用的了。接口描述了实现该接口的类必须具有的行为。“行为”是方法的集合。如果我们想创建多个信使,最简单的方法就是创建一个Messenger界面。每个使者需要什么?在基本层面上,他们必须能够接收和发送消息。

public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
现在我们可以简单地创建实现相应接口的信使类。编译器本身将“强制”我们在我们的类中实现它们。电报:

public class Telegram implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Telegram message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Telegram message!");
     }
}
微信:

public class WhatsApp implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a WhatsApp message!");
    }

     public void getMessage() {
         System.out.println("Reading a WhatsApp message!");
     }
}
微博:

public class Viber implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Viber message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Viber message!");
     }
}
这提供了哪些优势?其中最重要的是松耦合。想象一下,我们正在设计一个将收集客户数据的程序。该类Client肯定需要一个字段来指示客户端正在使用的特定信使。如果没有接口,这看起来很奇怪:

public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
我们创建了三个字段,但一个客户端只能有一个信使。我们只是不知道是哪一个。所以我们必须在类中添加每一种可能性,以便能够与客户端进行通信。事实证明,其中一两个将永远是null程序完全不需要的。最好改用我们的界面:

public class Client {

    private Messenger messenger;
}
这是一个松散耦合的例子!我们没有在类中指定特定的信使类Client,而只是表明客户端有一个信使。究竟是哪一个将在程序运行时确定。但是为什么我们需要接口呢?为什么他们甚至被添加到语言中?这是个好问题——也是正确的问题!我们不能使用普通的继承来达到相同的结果吗?该类Messenger作为父级,并且ViberTelegramWhatsApp作为子级。的确,这是可能的。但是有一个障碍。如您所知,Java 没有多重继承。但是有对多个接口的支持。一个类可以实现任意多个接口。想象一下,我们有一个Smartphone类有一个App字段,表示安装在智能手机上的应用程序。

public class Smartphone {

    private App app;
}
当然,应用程序和 Messenger 很相似,但它们仍然是不同的东西。Messenger 可以有移动版和桌面版,但 App 专门代表移动应用程序。事情是这样的——如果我们使用继承,我们将无法TelegramSmartphone类中添加对象。毕竟,该类Telegram不能同时继承AppMessenger!我们已经让它继承Messenger并添加到Client类中。但是Telegram类可以轻松实现这两个接口!因此,我们可以给Client类一个Telegram对象作为 a ,我们可以把它作为 anMessenger给类。这是你如何做到的: SmartphoneApp

public class Telegram implements Application, Messenger {

    // ...methods
}

public class Client {

    private Messenger messenger;

    public Client() {
        this.messenger = new Telegram();
    }
}


public class Smartphone {

    private Application application;

    public Smartphone() {
        this.application = new Telegram();
    }
}
现在我们正在使用Telegram我们想要的类。在某些地方,它充当App. 在其他地方,它充当Messenger. 您肯定已经注意到接口方法总是“空的”,即它们没有实现。原因很简单:接口描述了行为,但没有实现它。“所有实现CanSwim接口的对象都必须能够游泳”:这就是接口告诉我们的全部内容。鱼、鸭子和马游泳的具体方式是FishDuckHorse类,而不是接口。就像换频道是电视的任务一样。遥控器只是为您提供一个按钮。然而,Java 8 中出现了一个有趣的新增功能——默认方法。例如,您的接口有 10 个方法。其中 9 个在不同的类中有不同的实现,但一个对所有类的实现都是相同的。以前,在 Java 8 之前,接口方法没有任何实现:编译器立即给出错误。现在你可以这样做:

public interface CanSwim {

   public default void swim() {
       System.out.println("Swim!");
   }

   public void eat();

   public void run();
}
使用default关键字,我们创建了一个具有默认实现的接口方法。我们需要在所有eat()实现. 我们不需要用方法来做这个:每个类的实现都是一样的。顺便说一句,您已经在过去的任务中遇到过接口,即使您没有注意到 :) 这是一个生动的示例: 您已经使用了和接口!更准确地说,您已经使用了它们的实现 — 、、等。同一张图清楚地给出了一个示例,其中一个类同时实现了多个接口。例如,实现和run()CanSwimswim()为什么接口在 Java 中是必需的 - 2ListSetArrayListLinkedListHashSetLinkedListListDeque(双端队列)接口。您熟悉界面Map,或者更确切地说,熟悉它的HashMap实现。顺便说一句,这张图说明了一个特点:接口可以继承其他接口。接口SortedMap继承Map,同时Deque继承Queue。如果你想显示接口之间的关系,这是必要的,其中一个接口是另一个接口的扩展版本。 让我们考虑一个带有Queue接口的例子。我们还没有审核Queues,但它相当简单,就像商店里的普通队列或线路一样工作。您只能将项目添加到队列的末尾,并且只能从头开始取。在某些时候,开发人员需要队列的增强版本,以便在两端添加和获取项目。所以他们创建了一个Deque接口,它是一个双端队列。它具有普通队列的所有方法。毕竟是双端队列的父类,不过也增加了新的方法。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION