可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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:
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?
回答1:
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.
回答2:
What makes interfaces useful is not the fact that \"you can change your mind and use a different implementation later and only have to change the one place where the object is created\". That\'s a non-issue.
The real point is already in the name: they define an interface that anyone at all can implement to use all code that operates on that interface. The best example is java.util.Collections
which provides all kinds of useful methods that operate exclusively on interfaces, such as sort()
or reverse()
for List
. The point here is that this code can now be used to sort or reverse any class that implements the List
interfaces - not just ArrayList
and LinkedList
, but also classes that you write yourself, which may be implemented in a way the people who wrote java.util.Collections
never imagined.
In the same way, you can write code that operates on well-known interfaces, or interfaces you define, and other people can use your code without having to ask you to support their classes.
Another common use of interfaces is for Callbacks. For example, java.swing.table.TableCellRenderer, which allows you to influence how a Swing table displays the data in a certain column. You implement that interface, pass an instance to the JTable
, and at some point during the rendering of the table, your code will get called to do its stuff.
回答3:
One of the many uses I have read is where its difficult without multiple-inheritance-using-interfaces in Java :
class Animal
{
void walk() { }
....
.... //other methods and finally
void chew() { } //concentrate on this
}
Now, Imagine a case where:
class Reptile extends Animal
{
//reptile specific code here
} //not a problem here
but,
class Bird extends Animal
{
...... //other Bird specific code
} //now Birds cannot chew so this would a problem in the sense Bird classes can also call chew() method which is unwanted
Better design would be:
class Animal
{
void walk() { }
....
.... //other methods
}
Animal does not have the chew() method and instead is put in an interface as :
interface Chewable {
void chew();
}
and have Reptile class implement this and not Birds (since Birds cannot chew) :
class Reptile extends Animal implements Chewable { }
and incase of Birds simply:
class Bird extends Animal { }
回答4:
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.
回答5:
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.)
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:
|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.
The same Java code would look something like this (making the assumption that Duck and Cow are subclasses of 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.
In Smalltalk, we can write this:
|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.
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.)
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:
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.
回答6:
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.
The most important thing in Java is that you can implement multiple interfaces but you can only extend ONE class! Sample:
class Test extends Foo implements Comparable, Serializable, Formattable {
...
}
is possible but
class Test extends Foo, Bar, Buz {
...
}
is not!
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
.
回答7:
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.
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.
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.
回答8:
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
.
回答9:
WHY INTERFACE??????
It starts with a dog. In particular, a pug.
The pug has various behaviors:
public class Pug {
private String name;
public Pug(String n) { name = n; }
public String getName() { return name; }
public String bark() { return \"Arf!\"; }
public boolean hasCurlyTail() { return true; } }
And you have a Labrador, who also has a set of behaviors.
public class Lab {
private String name;
public Lab(String n) { name = n; }
public String getName() { return name; }
public String bark() { return \"Woof!\"; }
public boolean hasCurlyTail() { return false; } }
We can make some pugs and labs:
Pug pug = new Pug(\"Spot\");
Lab lab = new Lab(\"Fido\");
And we can invoke their behaviors:
pug.bark() -> \"Arf!\"
lab.bark() -> \"Woof!\"
pug.hasCurlyTail() -> true
lab.hasCurlyTail() -> false
pug.getName() -> \"Spot\"
Let\'s say I run a dog kennel and I need to keep track of all the dogs I\'m housing. I need to store my pugs and labradors in separate arrays:
public class Kennel {
Pug[] pugs = new Pug[10];
Lab[] labs = new Lab[10];
public void addPug(Pug p) { ... }
public void addLab(Lab l) { ... }
public void printDogs() { // Display names of all the dogs } }
But this is clearly not optimal. If I want to house some poodles, too, I have to change my Kennel definition to add an array of Poodles. In fact, I need a separate array for each kind of dog.
Insight: both pugs and labradors (and poodles) are types of dogs and they have the same set of behaviors. That is, we can say (for the purposes of this example) that all dogs can bark, have a name, and may or may not have a curly tail. We can use an interface to define what all dogs can do, but leave it up to the specific types of dogs to implement those particular behaviors. The interface says \"here are the things that all dogs can do\" but doesn\'t say how each behavior is done.
public interface Dog
{
public String bark();
public String getName();
public boolean hasCurlyTail(); }
Then I slightly alter the Pug and Lab classes to implement the Dog behaviors. We can say that a Pug is a Dog and a Lab is a dog.
public class Pug implements Dog {
// the rest is the same as before }
public class Lab implements Dog {
// the rest is the same as before
}
I can still instantiate Pugs and Labs as I previously did, but now I also get a new way to do it:
Dog d1 = new Pug(\"Spot\");
Dog d2 = new Lab(\"Fido\");
This says that d1 is not only a Dog, it\'s specifically a Pug. And d2 is also a Dog, specifically a Lab.
We can invoke the behaviors and they work as before:
d1.bark() -> \"Arf!\"
d2.bark() -> \"Woof!\"
d1.hasCurlyTail() -> true
d2.hasCurlyTail() -> false
d1.getName() -> \"Spot\"
Here\'s where all the extra work pays off. The Kennel class become much simpler. I need only one array and one addDog method. Both will work with any object that is a dog; that is, objects that implement the Dog interface.
public class Kennel {
Dog[] dogs = new Dog[20];
public void addDog(Dog d) { ... }
public void printDogs() {
// Display names of all the dogs } }
Here\'s how to use it:
Kennel k = new Kennel();
Dog d1 = new Pug(\"Spot\");
Dog d2 = new Lab(\"Fido\");
k.addDog(d1);
k.addDog(d2);
k.printDogs();
The last statement would display:
Spot Fido
An interface give you the ability to specify a set of behaviors that all classes that implement the interface will share in common. Consequently, we can define variables and collections (such as arrays) that don\'t have to know in advance what kind of specific object they will hold, only that they\'ll hold objects that implement the interface.
回答10:
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.
This makes functions like Collections.sort(List list)
possible, regardless of how the List
is implemented.
回答11:
This is the reason why Factory Patterns and 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\'t create an object in your method, which should be most of your code.
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).
回答12:
Don\'t forget that at a later date you can take an existing class, and make it implement IBox
, and it will then become available to all your box-aware code.
This becomes a bit clearer if interfaces are named -able. e.g.
public interface Saveable {
....
public interface Printable {
....
etc. (Naming schemes don\'t always work e.g. I\'m not sure Boxable
is appropriate here)
回答13:
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?
I am updating the answer with new features of interface, which have introduced with java 8 version.
From oracle documentation page on summary of interface :
An interface declaration can contain
- method signatures
- default methods
- static methods
- constant definitions.
The only methods that have implementations are default and static methods.
Uses of interface:
- To define a contract
- To link unrelated classes with has a capabilities (e.g. classes implementing
Serializable
interface may or may not have any relation between them except implementing that interface
- To provide interchangeable implementation e.g. strategy pattern
- Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces
- Organize helper methods in your libraries with static methods ( you can keep static methods specific to an interface in the same interface rather than in a separate class)
Some related SE questions with respect to difference between abstract class and interface and use cases with working examples:
What is the difference between an interface and abstract class?
How should I have explained the difference between an Interface and an Abstract class?
Have a look at documentation page to understand new features added in java 8 : default methods and static methods.
回答14:
The purpose of interfaces is abstraction, or decoupling from implementation.
If you introduce an abstraction in your program, you don\'t care about the possible implementations. You are interested in what it can do and not how, and you use an interface
to express this in Java.
回答15:
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).
回答16:
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.
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.
回答17:
Here is my understanding of interface advantage. Correct me if I am wrong.
Imagine we are developing OS and other team is developing the drivers for some devices.
So we have developed an interface StorageDevice. We have two implementations of it (FDD and HDD) provided by other developers team.
Then we have a OperatingSystem class which can call interface methods such as saveData by just passing an instance of class implemented the StorageDevice interface.
The advantage here is that we don\'t care about the implementation of the interface. The other team will do the job by implementing the StorageDevice interface.
package mypack;
interface StorageDevice {
void saveData (String data);
}
class FDD implements StorageDevice {
public void saveData (String data) {
System.out.println(\"Save to floppy drive! Data: \"+data);
}
}
class HDD implements StorageDevice {
public void saveData (String data) {
System.out.println(\"Save to hard disk drive! Data: \"+data);
}
}
class OperatingSystem {
public String name;
StorageDevice[] devices;
public OperatingSystem(String name, StorageDevice[] devices) {
this.name = name;
this.devices = devices.clone();
System.out.println(\"Running OS \" + this.name);
System.out.println(\"List with storage devices available:\");
for (StorageDevice s: devices) {
System.out.println(s);
}
}
public void saveSomeDataToStorageDevice (StorageDevice storage, String data) {
storage.saveData(data);
}
}
public class Main {
public static void main(String[] args) {
StorageDevice fdd0 = new FDD();
StorageDevice hdd0 = new HDD();
StorageDevice[] devs = {fdd0, hdd0};
OperatingSystem os = new OperatingSystem(\"Linux\", devs);
os.saveSomeDataToStorageDevice(fdd0, \"blah, blah, blah...\");
}
}