我曾多次看到有人提到这一点,但我不清楚这是什么意思。你什么时候,为什么要这么做?

我知道接口是做什么的,但我不清楚这一点的事实使我认为我错过了正确使用它们。

如果你要这样做

IInterface classRef = new ObjectWhatever()

你可以使用任何实现IInterface的类吗?你什么时候需要这样做?我能想到的唯一一件事是,如果你有一个方法,你不确定什么对象将被传递,除了它实现IInterface。我不知道你需要多久做一次。

另外,如何编写一个方法来接受实现接口的对象呢?这可能吗?


当前回答

下面是一个简单的示例,用于说明如何对航班预订系统进行编程。

//This interface is very flexible and abstract
    addPassenger(Plane seat, Ticket ticket); 

//Boeing is implementation of Plane
    addPassenger(Boeing747 seat, EconomyTicket ticket); 
    addPassenger(Cessna, BusinessClass ticket);


    addPassenger(J15, E87687); 

其他回答

为了补充现有的帖子,当开发人员同时在不同的组件上工作时,有时对接口进行编码有助于大型项目。您所需要做的就是预先定义接口并为它们编写代码,而其他开发人员则为您正在实现的接口编写代码。

我曾经给学生的一个具体例子是他们应该写作

List myList = new ArrayList(); // programming to the List interface

而不是

ArrayList myList = new ArrayList(); // this is bad

在一个短程序中,它们看起来完全相同,但如果在程序中继续使用myList 100次,就会开始看到区别。第一个声明确保只调用myList上由List接口定义的方法(因此没有ArrayList特定的方法)。如果您以这种方式对接口进行了编程,那么稍后您就可以确定您确实需要

List myList = new TreeList();

你只需要在这一点上修改代码。您已经知道,其余的代码不会因为更改实现而被破坏,因为您对接口进行了编程。

当您谈论方法参数和返回值时,好处甚至更明显(我认为)。举个例子:

public ArrayList doSomething(HashMap map);

该方法声明将您绑定到两个具体实现(ArrayList和HashMap)。一旦从其他代码调用该方法,对这些类型的任何更改都可能意味着您将不得不更改调用代码。最好是根据接口进行编程。

public List doSomething(Map map);

现在,不管您返回什么样的List,或者作为参数传入什么样的Map。在doSomething方法中所做的更改不会强制您更改调用代码。

短篇故事:一个邮递员被要求挨家挨户地接收写有地址的信封(信件、文件、支票、礼品卡、申请书、情书)。

假设没有封面,让邮递员挨家挨户地接收所有的东西,然后交给其他人,邮递员会感到困惑。

所以最好用封面(在我们的故事中是接口)包装它,然后他就会很好地完成他的工作。

现在邮差的工作只是收和送封皮(他不关心封皮里面有什么)。

创建一个接口类型,而不是实际类型,而是使用实际类型实现它。

创建一个接口意味着你的组件可以很容易地适应其余的代码

我给你们举个例子。

你有一个plane界面,如下所示。

interface Airplane{
    parkPlane();
    servicePlane();
}

假设在平面的Controller类中有这样的方法

parkPlane(Airplane plane)

and

servicePlane(Airplane plane)

在您的程序中实现。它不会破坏你的代码。 我的意思是,只要它接受参数plane,它就不需要改变。

因为它将接受任何飞机,不管实际类型,飞行员,高空飞行,战斗机等。

同样,在集合中:

列表> <飞机飞机;//会占用你所有的飞机。

下面的例子将帮助您理解。


你有一架战斗机来执行它,所以

public class Fighter implements Airplane {

    public void  parkPlane(){
        // Specific implementations for fighter plane to park
    }
    public void  servicePlane(){
        // Specific implementatoins for fighter plane to service.
    }
}

HighFlyer和其他类也是如此:

public class HighFlyer implements Airplane {

    public void  parkPlane(){
        // Specific implementations for HighFlyer plane to park
    }

    public void  servicePlane(){
        // specific implementatoins for HighFlyer plane to service.
    }
}

现在假设你的控制器类多次使用AirPlane,

假设你的控制器类是ControlPlane,如下所示,

public Class ControlPlane{ 
 AirPlane plane;
 // so much method with AirPlane reference are used here...
}

神奇的是,你可以在不改变ControlPlane类代码的情况下,尽可能多地创建新的AirPlane类型实例。

你可以添加一个实例…

JumboJetPlane // implementing AirPlane interface.
AirBus        // implementing AirPlane interface.

您也可以删除以前创建的类型的实例。

即使当我们不依赖于抽象时,面向接口编程也是有利的。

接口编程迫使我们使用对象的上下文适当的子集。这很有用,因为它:

防止我们做一些不合时宜的事,还有 让我们在将来安全地更改实现。

例如,考虑实现Friend和Employee接口的Person类。

class Person implements AbstractEmployee, AbstractFriend {
}

在这个人的生日的情况下,我们编程到朋友界面,以防止像对待员工一样对待这个人。

function party() {
    const friend: Friend = new Person("Kathryn");
    friend.HaveFun();
}

在这个人的工作环境中,我们对雇员界面进行编程,以防止模糊工作场所的边界。

function workplace() {
    const employee: Employee = new Person("Kathryn");
    employee.DoWork();
}

太好了。我们在不同的环境中都有适当的表现,我们的软件运行良好。

在遥远的未来,如果我们的业务改变为与狗打交道,我们可以相当容易地更改软件。首先,我们创建一个实现Friend和Employee的Dog类。然后,我们安全地将新的Person()更改为新的Dog()。即使两个函数都有数千行代码,这个简单的编辑也可以工作,因为我们知道以下是正确的:

Function party只使用Person的Friend子集。 函数workplace只使用Person的Employee子集。 类Dog实现了Friend和Employee接口。

另一方面,如果任何一方或工作场所都针对Person进行编程,就会有同时拥有特定于Person的代码的风险。从“人”改为“狗”需要我们梳理代码,删除任何“狗”不支持的“人”特定代码。

寓意:为接口编程可以帮助我们的代码适当地运行,并为更改做好准备。它还使我们的代码能够依赖于抽象,这带来了更多的好处。

有很多解释,但让它更简单。以List为例。可以使用as实现一个列表:

内部数组 链表 其他实现

通过构建一个接口,比如一个List。您只编写了List的定义或List在实际中的含义。

你可以在内部使用任何类型的实现比如数组实现。但是假设您希望由于某种原因(比如bug或性能)更改实现。然后你只需要改变声明List<String> ls = new ArrayList<String>()为List<String> ls = new LinkedList<String>()。

在代码的其他地方,你不需要改变任何东西;因为其他所有东西都建立在List的定义之上。