为什么要声明一个接口,然后用它在Java中实例化一个对象?

为什么要声明一个接口,然后用它在Java中实例化一个对象?,java,oop,interface,polymorphism,Java,Oop,Interface,Polymorphism,我和一个朋友正在学习Java。今天我们讨论了接口,并讨论了如何使用接口 我朋友给我看的示例代码包含以下内容: IVehicle modeOfTransport1 = new Car(); IVehicle modeOfTransport2 = new Bike(); 其中iVihicle是一个在car和bike类中都实现的接口。 当定义一个接受IVehille作为参数的方法时,可以使用接口方法,当运行代码时,上述对象正常工作。但是,当您按照通常的要求申报汽车和自行车时,这一点非常有效: Car

我和一个朋友正在学习Java。今天我们讨论了接口,并讨论了如何使用接口

我朋友给我看的示例代码包含以下内容:

IVehicle modeOfTransport1 = new Car();
IVehicle modeOfTransport2 = new Bike();
其中iVihicle是一个在car和bike类中都实现的接口。 当定义一个接受IVehille作为参数的方法时,可以使用接口方法,当运行代码时,上述对象正常工作。但是,当您按照通常的要求申报汽车和自行车时,这一点非常有效:

Car modeOfTransport1 = new Car();
Bike modeOfTransport2 = new Bike();
public void (IVehicle myVehicle)
public void (IVehicle myVehicle)
{
    myVehicle.run() //This calls the implementation for that particular vehicle.
}

所以,我的问题是-在声明和实例化modeOfTransport对象时,为什么要使用前一种方法而不是后一种方法?这有关系吗?

因为您并不真正关心实现是什么。。。只有它的行为是什么

假设你有一只动物

interface Animal {
    String speak();
}

class Cat implements Animal {

    void claw(Furniture f) { /* code here */ }

    public String speak() { return "Meow!" }
}

class Dog implements Animal {

    void water(FireHydrant fh) { /* code here */ }

    public String speak() { return "Woof!"; }
}
现在你想给你的孩子一个宠物

Animal pet = new ...?
kid.give(pet);
你以后再拿回来

Animal pet = kid.getAnimal();
你不会想去的

pet.claw(favorateChair);
因为你不知道那孩子有没有狗。你不在乎。你只知道——动物——可以说话。你对它们与家具或消防栓的相互作用一无所知。你知道动物是用来说话的。这会让你的女儿咯咯笑(或者不笑!)

有了这个,当你做一条金鱼时,孩子的反应是非常糟糕的(原来金鱼不会说话!),但是当你放一只熊进去时,孩子的反应是非常可怕的

如果你说

Cat cat = new Cat();

因为您将自己限制在一只猫的能力范围内。

使用接口声明它们有一个很大的好处,这就是所谓的“向接口编码”,而不是“向实现编码”,这是一个大原则,这样您可以声明如下方法:

Car modeOfTransport1 = new Car();
Bike modeOfTransport2 = new Bike();
public void (IVehicle myVehicle)
public void (IVehicle myVehicle)
{
    myVehicle.run() //This calls the implementation for that particular vehicle.
}
这将接受实现该接口的任何对象,然后在运行时调用该实现,如下所示:

Car modeOfTransport1 = new Car();
Bike modeOfTransport2 = new Bike();
public void (IVehicle myVehicle)
public void (IVehicle myVehicle)
{
    myVehicle.run() //This calls the implementation for that particular vehicle.
}
要回答最初的问题,为什么要使用一个而不是另一个,有以下几个原因:

1) 使用接口声明它们,意味着您以后可以用实现该接口的任何其他具体类替换该值,而不是锁定到该特定的具体类中

2) 您可以通过使用接口声明多态性来充分利用多态性,因为每个实现都可以在运行时调用正确的方法

3) 您遵循代码的OOD原则创建接口

编程到接口而不是实现

当你对一个接口编程时,你将编写能处理任何类型车辆的代码。因此,在未来,你的代码,没有修改,应该与火车和飞机

如果你忽略了这个界面,那么你就会被汽车和自行车所困扰,任何新车都需要额外的代码修改

这背后的原则是:

对扩展开放,对修改关闭


声明接口并用对象实例化它们允许一个强大的概念,称为

List List=new ArrayList();
列表。添加(新车());
添加(新自行车());
对于(int i=0;i

明确回答这个问题:您将使用接口声明(即使您知道具体类型),以便可以将多个类型(实现相同接口)传递给方法或集合;然后,无论实际的类型是什么,都可以调用每个实现类型的公共行为。

这并不重要

真正重要的是需要在IVehicle上操作的其他接口。如果它们接受参数并返回值作为IVehicle,那么代码将更容易扩展

正如您所注意到的,这些对象中的任何一个都可以传递给接受IVehicle作为参数的方法


如果您的后续代码使用了所使用的特定于汽车或自行车的操作,那么将它们声明为汽车或自行车将是有利的。每个相关对象都可以使用特定于汽车和自行车的操作,并且两者都可以作为IVehicle使用(即可以传递)。

你真的在问:我应该使用什么参考类型

通常,您希望使用尽可能通用的引用类型,该类型仍然允许您访问所需的行为。这意味着您的具体类型的任何接口或父类,而不是具体类型本身。当然,不要把这一点看得太远——例如,您当然不想将所有内容声明为
对象

考虑以下选项:

Set<String> values1 = new TreeSet<String>();
TreeSet<String> values2 = new TreeSet<String>();
SortedSet<String> values3 = new TreeSet<String>();
注意不要使用第二个选项
values2
。它允许您使用
TreeSet
实现的特定行为,从而使得在
Set
的不同实现中进行交换变得更加困难。这很好,只要这是你的目标。因此,在您的示例中,仅当您需要访问不在
iVihicle
界面中的内容时,才使用
Car
Bike
引用。但请注意,以下操作不起作用:

TreeSet<String> values2 = new HashSet<String>(); // does not compile!

关于引用类型的问题不仅适用于声明变量的情况,也适用于必须指定每个参数类型的方法。幸运的是,“尽可能普遍地使用引用类型”的经验法则也适用于方法参数。

老实说,你的论点相当没有意义。这里发生的是隐式转换为
IVehicle
。您和您的朋友似乎在争论是否最好立即执行(根据第一个代码清单),还是稍后执行(当您调用该方法时,根据第二个代码清单)。无论哪种方式,它都将被隐式转换为
IVehicle
,所以真正的问题是——你需要处理一辆车,还是仅仅是一辆车?如果你只需要一个艾维