Java中的接口 - 它们用于什么?

时间:2012-12-25 21:07:52

标签: java interface

  

可能重复:
  The purpose of interfaces continued

我刚刚开始学习Java 我遇到了Interfaces,我知道该如何使用,但仍然无法完全掌握它的想法 据我了解,interfaces通常由类实现,然后必须实现接口中声明的方法 问题是 - 究竟是什么意思?从接口实现方法作为普通类方法不是更容易吗?使用接口的优势究竟是什么?

我试过在谷歌上寻找答案。有很多,但我仍然无法理解它的意义。我还阅读了this question及其答案,但整个合同事情让它变得更加复杂......

希望有人可以简化它! :)
提前谢谢!

9 个答案:

答案 0 :(得分:11)

接口允许您在运行时提供不同的实现,注入依赖关系,单独关注,使用不同的实现进行测试。

只需将接口视为类保证实现的合同即可。实现接口的具体类是无关紧要的。不知道这是否有帮助。

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

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. 

}

}

答案 1 :(得分:8)

接口可用于许多事情。最常用的是多态dependency injection,您可以在运行时更改依赖关系。例如,假设您有一个名为Database的接口,它有一个名为getField(...)的方法。

public interface Database 
{
    public void getField(String field);
}

现在假设您在应用程序中使用了两个数据库,具体取决于您的客户端:MySQL和PostgreSQL。你将有两个具体的课程:

public class MySQL implements Database
{
    // mysql connection specific code
    @Override
    public void getField(String field)
    {
        // retrieve value from MySQL
    }
}

和...

public class PostgreSQL implements Database
{
    // postgre connection specific code
    @Override
    public String getField(String field)
    {
        // retrieve value from Postgre
    }
}

现在,根据您的客户端偏好,您可以在主

中实例化MySQL或PostgreSQL类
 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使用Factory / Abstract Factory或脚本引擎。

答案 2 :(得分:4)

他们要实现polymorphism而不需要使用继承。

答案 3 :(得分:3)

接口的想法是指定一个类可以实现的契约。你读过的另一篇文章大致涵盖了这个想法。

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

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

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

如果我们没有简洁的类型名称,那么该方法必须明确列出要求,这不是很容易,特别是当接口“contract”有很多方法时。

这也是另一种方式。通过使用特定关键字“实现AutoCloseable”来表示意图,编译器可以告诉您是否未正确实现合同。

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

}

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

其他一些语言(Python就是一个很好的例子)不要这样做 - 而是按照你在问题中建议的方式做事。这被称为“鸭子打字”(如果它看起来像一只鸭子,像鸭子一样呱呱叫,像鸭子一样对待它),它也是一种可行的做事方式,与Java不同。

答案 4 :(得分:2)

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

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

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

答案 5 :(得分:2)

例如,使用List<T>界面。您只能在一个点决定是否要在特定场合使用ArrayList<T>LinkedList<T>(或其他相关内容)。

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

答案 6 :(得分:2)

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

您在允许多继承的编程语言中编写类(名称:Parent)。该类包含一个实例变量(int a;)和一个方法(void hello())。然后创建两个类(Kid1Kid2)。每个类都延伸Parent class。然后,您在hello();Kid1类中覆盖Kid2方法,并为a变量指定一些值,Kid1Kid2也是如此类。在最后一步中,您将创建扩展MainKid1类(多重继承)的第4个类(如Kid2)。现在......问题是,hello();方法将Main类继承哪个,以及a变量的值。

由于Java中没有多重继承,我们有interfaces ...包含抽象方法的抽象类。我们可以实现多个接口,但在条件下我们必须覆盖实现interface包含的所有方法。

答案 7 :(得分:1)

我建议你看一下Strategy Pattern和作文模式。

答案 8 :(得分:1)

你的问题可以在一篇文章中广泛回答,而不是过于拙劣或过于通用而没有用处。因此,我将尝试给出一个有希望的有用的例子,说明接口何时非常有用。

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

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

现在你的命令对象的函数变得很简单,因为它只能依赖于它需要比较的任何类对象具有相同的Comapre方法这一事实。

这只是一个例子 - 而且是一个相当普遍的例子。