Java 除了拥有正确的方法之外,接口还有更多的意义吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/504904/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there more to an interface than having the correct methods
提问by Click Upvote
So lets say I have this interface:
所以可以说我有这个界面:
public interface IBox
{
public void setSize(int size);
public int getSize();
public int getArea();
//...and so on
}
And I have a class that implements it:
我有一个实现它的类:
public class Rectangle implements IBox
{
private int size;
//Methods here
}
If I wanted to use the interface IBox, i can't actually create an instance of it, in the way:
如果我想使用 IBox 接口,我实际上无法创建它的实例,方式如下:
public static void main(String args[])
{
Ibox myBox=new Ibox();
}
right? So I'd actually have to do this:
对?所以我实际上必须这样做:
public static void main(String args[])
{
Rectangle myBox=new Rectangle();
}
If that's true, then the only purpose of interfaces is to make sure that the class which implements an interface has got the correct methods in it as described by an interface? Or is there any other use of interfaces?
如果这是真的,那么接口的唯一目的是确保实现接口的类具有接口描述的正确方法?或者还有其他用途的接口吗?
采纳答案by morgancodes
Interfaces are a way to make your code more flexible. What you do is this:
接口是使您的代码更灵活的一种方式。你要做的是:
Ibox myBox=new Rectangle();
Then, later, if you decide you want to use a different kind of box (maybe there's another library, with a better kind of box), you switch your code to:
然后,稍后,如果您决定要使用不同类型的框(也许还有另一个库,具有更好类型的框),则将代码切换为:
Ibox myBox=new OtherKindOfBox();
Once you get used to it, you'll find it's a great (actually essential) way to work.
一旦你习惯了它,你会发现它是一种很好的(实际上必不可少的)工作方式。
Another reason is, for example, if you want to create a list of boxes and perform some operation on each one, but you want the list to contain different kinds of boxes. On each box you could do:
另一个原因是,例如,如果您想创建一个框列表并对每个框执行一些操作,但您希望列表包含不同类型的框。在每个盒子上,您可以执行以下操作:
myBox.close()
(assuming IBox has a close() method) even though the actual class of myBox changes depending on which box you're at in the iteration.
(假设 IBox 有 close() 方法)即使 myBox 的实际类会根据您在迭代中所处的框而变化。
回答by IAdapter
you could do
你可以
Ibox myBox = new Rectangle();
that way you are using this object as Ibox and you don't care that its really Rectangle
.
这样您就可以将此对象用作 Ibox,而您并不关心它是否真的Rectangle
.
回答by Johannes Weiss
Normally Interfaces define the interface you should use (as the name says it ;-) ). Sample
通常接口定义你应该使用的接口(正如名字所说的;-))。样本
public void foo(List l) {
... do something
}
Now your function foo
accepts ArrayList
s, LinkedList
s, ... not only one type.
现在您的函数foo
接受ArrayList
s, LinkedList
s, ... 不仅仅是一种类型。
The most important thing in Java is that you can implement multiple interfaces but you can only extend ONE class! Sample:
Java 中最重要的是你可以实现多个接口,但你只能扩展一个类!样本:
class Test extends Foo implements Comparable, Serializable, Formattable {
...
}
是可能的,但是
class Test extends Foo, Bar, Buz {
...
}
不是!
Your code above could also be: IBox myBox = new Rectangle();
. The important thing is now, that myBox ONLY contains the methods/fields from IBox and not the (possibly existing) other methods from Rectangle
.
你上面的代码也可能是:IBox myBox = new Rectangle();
。现在重要的是,myBox 只包含来自 IBox 的方法/字段,而不包含来自Rectangle
.
回答by Todd R
If you have CardboardBox and HtmlBox (both of which implement IBox), you can pass both of them to any method that accepts a IBox. Even though they are both very different and not completely interchangable, methods that don't care about "open" or "resize" can still use your classes (perhaps because they care about how many pixels are needed to display something on a screen).
如果你有 CardboardBox 和 HtmlBox(两者都实现了 IBox),你可以将它们传递给任何接受 IBox 的方法。尽管它们非常不同且不能完全互换,但不关心“打开”或“调整大小”的方法仍然可以使用您的类(可能是因为它们关心在屏幕上显示内容需要多少像素)。
回答by Apocalisp
The purpose of interfaces is polymorphism, a.k.a. type substitution. For example, given the following method:
接口的目的是多态性,也就是类型替换。例如,给定以下方法:
public void scale(IBox b, int i) {
b.setSize(b.getSize() * i);
}
When calling the scale
method, you can provide any value that is of a type that implements the IBox
interface. In other words, if Rectangle
and Square
both implement IBox
, you can provide either a Rectangle
or a Square
wherever an IBox
is expected.
调用该scale
方法时,您可以提供属于实现该IBox
接口的类型的任何值。换句话说,如果Rectangle
和Square
两者都实现IBox
,您可以在预期Rectangle
的Square
任何地方提供 a或 a IBox
。
回答by Kip
A great example of how interfaces are used is in the Collections framework. If you write a function that takes a List
, then it doesn't matter if the user passes in a Vector
or an ArrayList
or a HashList
or whatever. And you can pass that List
to any function requiring a Collection
or Iterable
interface too.
集合框架是如何使用接口的一个很好的例子。如果您编写一个接受 a 的函数List
,那么用户是否传入 aVector
或 anArrayList
或 aHashList
或其他无关紧要。您也可以将其传递List
给任何需要 aCollection
或Iterable
接口的函数。
This makes functions like Collections.sort(List list)
possible, regardless of how the List
is implemented.
Collections.sort(List list)
无论是如何实现的,这都使功能成为可能List
。
回答by mandel
Interfaces where a fetature added to java to allow multiple inheritance. The developers of Java though/realized that having multiple inheritance was a "dangerous" feature, that is why the came up with the idea of an interface.
将特征添加到 java 以允许多重继承的接口。Java 的开发人员虽然/意识到具有多重继承是一个“危险”的特性,这就是为什么提出接口的想法。
multiple inheritance is dangerous because you might have a class like the following:
多重继承很危险,因为您可能有一个如下所示的类:
class Box{
public int getSize(){
return 0;
}
public int getArea(){
return 1;
}
}
class Triangle{
public int getSize(){
return 1;
}
public int getArea(){
return 0;
}
}
class FunckyFigure extends Box, Triable{
// we do not implement the methods we will used the inherited ones
}
Which would be the method that should be called when we use
哪个是我们使用时应该调用的方法
FunckyFigure.GetArea();
All the problems are solved with interfaces, because you do know you can extend the interfaces and that they wont have classing methods... ofcourse the compiler is nice and tells you if you did not implemented a methods, but I like to think that is a side effect of a more interesting idea.
所有的问题都用接口解决了,因为你知道你可以扩展接口并且它们不会有分类方法......当然编译器很好并且会告诉你你是否没有实现方法,但我喜欢认为这是一个更有趣的想法的副作用。
回答by Jared
Interfaces allow statically typed languages to support polymorphism. An Object Oriented purist would insist that a language should provide inheritance, encapsulation, modularity and polymorphism in order to be a fully-featured Object Oriented language. In dynamically-typed - or duck typed - languages (like Smalltalk,) polymorphism is trivial; however, in statically typed languages (like Java or C#,) polymorphism is far from trivial (in fact, on the surface it seems to be at odds with the notion of strong typing.)
接口允许静态类型语言支持多态。一个面向对象的纯粹主义者会坚持认为一种语言应该提供继承、封装、模块化和多态性,以便成为一个功能齐全的面向对象语言。在动态类型或鸭子类型的语言(如 Smalltalk)中,多态性是微不足道的;然而,在静态类型语言(如 Java 或 C#)中,多态性远非微不足道(实际上,从表面上看,它似乎与强类型的概念不一致。)
Let me demonstrate:
让我演示一下:
In a dynamically-typed (or duck typed) language (like Smalltalk), all variables are references to objects (nothing less and nothing more.) So, in Smalltalk, I can do this:
在动态类型(或鸭子类型)语言(如 Smalltalk)中,所有变量都是对对象的引用(仅此而已。)因此,在 Smalltalk 中,我可以这样做:
|anAnimal|
anAnimal := Pig new.
anAnimal makeNoise.
anAnimal := Cow new.
anAnimal makeNoise.
That code:
那个代码:
- Declares a local variable called anAnimal (note that we DO NOT specify the TYPE of the variable - all variables are references to an object, no more and no less.)
- Creates a new instance of the class named "Pig"
- Assigns that new instance of Pig to the variable anAnimal.
- Sends the message
makeNoise
to the pig. - Repeats the whole thing using a cow, but assigning it to the same exact variable as the Pig.
- 声明一个名为 anAnimal 的局部变量(请注意,我们不指定变量的 TYPE - 所有变量都是对对象的引用,不多也不少。)
- 创建名为“Pig”的类的新实例
- 将 Pig 的新实例分配给变量 anAnimal。
- 将消息发送
makeNoise
给猪。 - 使用母牛重复整个过程,但将其分配给与猪相同的变量。
The same Java code would look something like this (making the assumption that Duck and Cow are subclasses of Animal:
相同的 Java 代码看起来像这样(假设 Duck 和 Cow 是 Animal 的子类:
Animal anAnimal = new Pig();
duck.makeNoise();
anAnimal = new Cow();
cow.makeNoise();
That's all well and good, until we introduce class Vegetable. Vegetables have some of the same behavior as Animal, but not all. For example, both Animal and Vegetable might be able to grow, but clearly vegetables don't make noise and animals cannot be harvested.
这一切都很好,直到我们介绍蔬菜类。蔬菜有一些与动物相同的行为,但不是全部。例如,Animal 和Vegetable 可能都可以生长,但很明显,蔬菜不会发出噪音,并且不能收获动物。
In Smalltalk, we can write this:
在 Smalltalk 中,我们可以这样写:
|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.
aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.
This works perfectly well in Smalltalk because it is duck-typed (if it walks like a duck, and quacks like a duck - it is a duck.) In this case, when a message is sent to an object, a lookup is performed on the receiver's method list, and if a matching method is found, it is called. If not, some kind of NoSuchMethodError exception is thrown - but it's all done at runtime.
这在 Smalltalk 中非常有效,因为它是鸭子类型的(如果它像鸭子一样走路,像鸭子一样嘎嘎叫 - 它就是鸭子。)在这种情况下,当消息发送到对象时,会在接收者的方法列表,如果找到匹配的方法,则调用它。如果没有,就会抛出某种 NoSuchMethodError 异常——但这一切都是在运行时完成的。
But in Java, a statically typed language, what type can we assign to our variable? Corn needs to inherit from Vegetable, to support grow, but cannot inherit from Animal, because it does not make noise. Cow needs to inherit from Animal to support makeNoise, but cannot inherit from Vegetable because it should not implement harvest. It looks like we need multiple inheritance- the ability to inherit from more than one class. But that turns out to be a pretty difficult language feature because of all the edge cases that pop up (what happens when more than one parallel superclass implement the same method?, etc.)
但是在 Java 这种静态类型语言中,我们可以为变量分配什么类型?玉米需要从Vegetable继承,支持grow,但不能从Animal继承,因为它不会发出噪音。Cow 需要从 Animal 继承来支持 makeNoise,但不能从Vegetable 继承,因为它不应该实现收获。看起来我们需要多重继承——从多个类继承的能力。但结果证明这是一个非常困难的语言特性,因为会出现所有边缘情况(当多个并行超类实现相同的方法时会发生什么?等)
Along come interfaces...
随之而来的接口...
If we make Animal and Vegetable classes, with each implementing Growable, we can declare that our Cow is Animal and our Corn is Vegetable. We can also declare that both Animal and Vegetable are Growable. That lets us write this to grow everything:
如果我们创建 Animal 和 Vegetable 类,每个类都实现 Growable,我们可以声明我们的 Cow 是 Animal 而我们的 Corn 是Vegetable。我们也可以声明Animal 和Vegetable 都是Growable。这让我们写这个来增长一切:
List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());
for(Growable g : list) {
g.grow();
}
And it lets us do this, to make animal noises:
它让我们这样做,以发出动物的声音:
List<Animal> list = new ArrayList<Animal>();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
a.makeNoise();
}
The advantage to the duck-typed language is that you get really nice polymorphism: all a class has to do to provide behavior is provide the method. As long as everyone plays nice, and only sends messages that match defined methods, all is good. The downside is that the kind of error below isn't caught until runtime:
鸭子类型语言的优势在于您可以获得非常好的多态性:类为提供行为所要做的就是提供方法。只要每个人都表现得很好,并且只发送与定义的方法匹配的消息,一切都很好。缺点是直到运行时才会捕获下面的错误类型:
|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.
Statically-typed languages provide much better "programming by contract," because they will catch the two kinds of error below at compile-time:
静态类型语言提供了更好的“契约式编程”,因为它们会在编译时捕获以下两种错误:
// Compiler error: Corn cannot be cast to Animal.
Animal farmObject = new Corn();
farmObject makeNoise();
--
——
// Compiler error: Animal doesn't have the harvest message.
Animal farmObject = new Cow();
farmObject.harvest();
So....to summarize:
所以……总结一下:
Interface implementation allows you to specify what kinds of things objects can do (interaction) and Class inheritance lets you specify how things should be done (implementation).
Interfaces give us many of the benefits of "true" polymorphism, without sacrificing compiler type checking.
接口实现允许你指定对象可以做什么(交互),类继承允许你指定应该如何做(实现)。
接口为我们提供了“真正的”多态的许多好处,同时又不牺牲编译器类型检查。
回答by Christopher Smith
This is the reason why Factory Patternsand other creational patterns are so popular in Java. You are correct that without them Java doesn't provide an out of the box mechanism for easy abstraction of instantiation. Still, you get abstraction everywhere where you don'tcreate an object in your method, which should be most of your code.
这就是工厂模式和其他创建模式在 Java 中如此流行的原因。你是对的,如果没有它们,Java 不会提供一个开箱即用的机制来轻松抽象实例化。尽管如此,您在没有在方法中创建对象的任何地方都会获得抽象,这应该是您的大部分代码。
As an aside, I generally encourage people to not follow the "IRealname" mechanism for naming interfaces. That's a Windows/COM thing that puts one foot in the grave of Hungarian notation and really isn't necessary (Java is already strongly typed, and the whole point of having interfaces is to have them as largely indistinguishable from class types as possible).
顺便说一句,我通常鼓励人们不要遵循“IRealname”机制来命名接口。这是一个 Windows/COM 的东西,它把一只脚放在匈牙利符号的坟墓里,真的没有必要(Java 已经是强类型的,拥有接口的全部意义在于让它们尽可能与类类型无法区分)。
回答by Ickster
I think you understand everything Interfaces do, but you're not yet imagining the situations in which an Interface is useful.
我认为您了解接口所做的一切,但您还没有想象接口有用的情况。
If you're instantiating, using and releasing an object all within a narrow scope (for example, within one method call), an Interface doesn't really add anything. Like you noted, the concrete class is known.
如果您在一个狭窄的范围内(例如,在一个方法调用内)实例化、使用和释放一个对象,则接口实际上不会添加任何内容。正如您所指出的,具体类是已知的。
Where Interfaces are useful is when an object needs to be created one place and returned to a caller that may not care about the implementation details. Let's change your IBox example to an Shape. Now we can have implementations of Shape such as Rectangle, Circle, Triangle, etc., The implementations of the getArea() and getSize() methods will be completely different for each concrete class.
接口有用的地方是当一个对象需要在一个地方创建并返回给可能不关心实现细节的调用者时。让我们将您的 IBox 示例更改为 Shape。现在我们可以有 Rectangle、Circle、Triangle 等 Shape 的实现,每个具体类的 getArea() 和 getSize() 方法的实现将完全不同。
Now you can use a factory with a variety of createShape(params) methods which will return an appropriate Shape depending on the params passed in. Obviously, the factory will know about what type of Shape is being created, but the caller won't have to care about whether it's a circle, or a square, or so on.
现在,您可以使用具有各种 createShape(params) 方法的工厂,这些方法将根据传入的参数返回适当的 Shape。显然,工厂将知道正在创建的 Shape 类型,但调用者不会关心它是圆形,还是正方形,等等。
Now, imagine you have a variety of operations you have to perform on your shapes. Maybe you need to sort them by area, set them all to a new size, and then display them in a UI. The Shapes are all created by the factory and then can be passed to the Sorter, Sizer and Display classes very easily. If you need to add a hexagon class some time in the future, you don't have to change anything but the factory. Without the Interface, adding another shape becomes a very messy process.
现在,假设您必须对形状执行多种操作。也许您需要按区域对它们进行排序,将它们全部设置为新的大小,然后在 UI 中显示它们。Shapes 都是由工厂创建的,然后可以很容易地传递给 Sorter、Sizer 和 Display 类。如果将来某个时候需要添加一个六边形类,除了工厂之外,你不需要改变任何东西。如果没有界面,添加另一个形状将成为一个非常混乱的过程。