关于Java中的接口:Java中的接口它们是为了什么?

Interfaces in Java - what are they for?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
The purpose of interfaces continued

不久前我刚开始学习Java。我遇到过Interfaces,我知道如何使用它,但仍然不能完全理解它的概念。据我所知,Interfaces通常由类实现,然后这些类必须实现接口中声明的方法。问题是-到底有什么意义?仅仅将接口中的方法实现为普通类方法会更容易吗?使用接口到底有什么好处?

我试着在谷歌上寻找答案。有很多,但我还是不明白它的意义。我也读过这个问题及其答案,但整个合同的事情只是让它变得更复杂…

希望有人能把它简化得足够好!:)事先谢谢!


接口允许您在运行时提供不同的实现、注入依赖项、分离关注点、使用不同的实现进行测试。

只需将接口看作类保证实现的契约。实现接口的具体类是不相关的。不知道这是否有帮助。

发现一些代码可能有帮助。这并不能解释所有的接口,继续阅读,但我希望这能让你开始。这里的要点是您可以改变实现…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package stack.overflow.example;

public interface IExampleService {
void callExpensiveService();
}

public class TestService implements IExampleService {

@Override
public void callExpensiveService() {
    // This is a mock service, we run this as many
    // times as we like for free to test our software

}
}

public class ExpensiveService implements IExampleService {
@Override
public void callExpensiveService() {
    // This performs some really expensive service,
    // Ideally this will only happen in the field
    // We'd rather test as much of our software for
    // free if possible.
}
}


public class Main {

/**
 * @param args
 */

public static void main(String[] args) {

    // In a test program I might write
    IExampleService testService = new TestService();
    testService.callExpensiveService();

    // Alternatively, in a real program I might write
    IExampleService testService = new ExpensiveService();
    testService.callExpensiveService();

    // The difference above, is that we can vary the concrete
    // class which is instantiated. In reality we can use a
    // service locator, or use dependency injection to determine
    // at runtime which class to implement.

    // So in the above example my testing can be done for free, but
    // real world users would still be charged. Point is that the interface
    // provides a contract that we know will always be fulfilled, regardless
    // of the implementation.

}

}


接口可以用于许多事情。最常见的用法是多态性和依赖注入,您可以在运行时更改依赖项。例如,假设您有一个名为database的接口,它有一个名为getField(...)的方法。

1
2
3
4
public interface Database
{
    public void getField(String field);
}

现在假设您在应用程序中使用了两个数据库,这取决于您的客户机:mysql和postgresql。您将有两个具体类别:

1
2
3
4
5
6
7
8
9
public class MySQL implements Database
{
    // mysql connection specific code
    @Override
    public void getField(String field)
    {
        // retrieve value from MySQL
    }
}

还有…

1
2
3
4
5
6
7
8
9
public class PostgreSQL implements Database
{
    // postgre connection specific code
    @Override
    public String getField(String field)
    {
        // retrieve value from Postgre
    }
}

现在,根据您的客户机偏好,您可以在主目录中实例化mysql或postgresql类。

1
2
3
4
5
6
7
8
9
10
 public static void main(String args[])
 {
     if (args[2].equals("mysql"))
         Database db = new MySQL();
     else
         Database db = new PostgreSQL();
 }

 //now get the field
 String foo = db.getField();

也可以将工厂/抽象工厂或脚本引擎与JS或Ruby一起使用。


它们用于实现多态性而不需要使用继承。


接口的概念是它们指定一个类可以实现的契约。这个想法在你读到的另一篇文章中有很大的介绍。

直接从接口实现方法还不够的原因是其他方法可以使用接口类型来要求这些方法存在。

让我们以autocloseable为例。唯一的方法是一个close方法,但是对于一个方法来说,使用类型表达"我需要一个可以关闭的参数"的思想要比列出所有方法容易得多:

1
2
3
4
public void passMeSomethingThatICanClose(AutoCloseable bar) {
    //stuff goes here
    bar.close(); //the compiler knows this call is possible
}

如果没有简洁的类型名,方法就必须显式地列出需求,这并不容易,特别是当接口"contract"有许多方法时。

这也适用于其他方面。通过使用一个特定的关键字"implementsautoclosable"来表示意图,编译器可以告诉您是否没有正确地实现契约。

1
2
3
4
public class BrokenCloseable implements AutoCloseable {
    public void colse() {  //ERROR — see the typo?
        //blah
    }

}

这是一个错误,因为方法的名称错误。在Java中,编译器可以(也会)告诉你这一点。但是,如果您只是自己负责实现这些方法,就不会出现错误。相反,你的班级只是默默地"不是"自动关闭。

其他一些语言(Python就是一个很好的例子)不会这样做——而是按照您在问题中建议的方式来做。这被称为"鸭子打字"(如果它看起来像鸭子,像鸭子一样嘎嘎叫,像鸭子一样对待它),它也是一种可行的方法,和Java不同。


因为"致命的死亡钻石"。让我们考虑一下这种情况:

在允许多重继承的情况下,可以用编程语言编写类(名称:Parent)。该类包含一个实例变量(int a;和一个方法(void hello())。然后创建两个类(Kid1Kid2)。每个类都扩展了Parent class。然后在Kid1Kid2类中重写hello();方法,并为a变量赋值,也为Kid1Kid2类赋值。在最后一步中,您创建扩展Kid1Kid2类(多重继承)的第4类(如Main)。现在……问题是,hello();方法中哪个将继承Main类,哪个值将继承a变量。

由于Java中没有多重继承,所以我们有EDCOX1,18…包含抽象方法的抽象类。我们可以实现多个接口,但在这种情况下,我们必须重写实现interface所包含的所有方法。


List接口为例。您只能在一个点上决定是否要在特定的场合使用ArrayListLinkedList或其他相关的东西。

然而,它们都实现了List接口。因此,无论它们是如何工作的,或者它们是否在层次结构上相关,都可以保证它们公开了一组您可以使用的方法,并且您不必在代码的每一点上都知道最终使用的集合对象背后的实现。


接口指定功能。在某种程度上,它们提供了多重继承。假设我有一个函数可以用一个列表来做一些事情。但不只是一个列表,只是任何集合。任何我可以循环的内容。所以我使用了接口iterable。

1
2
3
public int randomMethod(Iterable<Integer> li) {
...
}

现在,它与列表和哈希集以及所有不同的东西一起工作,这些东西是以完全不同的方式实现的,并且属于不同的类结构,但是所有这些都提供了这个基本功能。这与say不同,say是一个大型的游戏循环,.update()是所有扩展公共类的实体,尽管可以使用接口。


你的问题是如何宽泛地回答一篇文章,而不是太做作或太笼统,而不是有用。因此,我将尝试给出一个有希望有意义的例子,说明什么时候接口非常有用。

假设有许多类表示各种类型的对象。可能是一打,或者任何足够大的数字。在另一个类中有一些函数,希望能够对任意对象排序。为此,它需要能够比较任何两个对象,并确定一个对象是大于或小于另一个对象。由于每个对象可以是许多类中的任何一个,所以编写这个比较函数会非常繁琐,因为它必须确定每个对象的类型,然后通过为每个类调用适当的逻辑来进行比较。

输入接口。此时,您可以让所有类实现一个接口,比如Comparable,它指定实现它的任何类都将实现一个Compare方法。

现在,您的命令对象的函数将变得很容易编写,因为它只能依赖于这样一个事实:它需要比较的任何对象类都将具有相同的comapre方法。

这只是一个例子——而且是一个相当常见的例子。


我建议你看一下策略模式和组合模式。