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

10

可能是重复问题:
接口的目的续

我不久前开始学习Java。
我遇到了接口(Interfaces),我知道如何使用它们,但仍无法完全理解它们的概念。
据我所知,接口通常由类实现,然后必须实现接口中声明的方法。
问题是-这有什么用?直接实现接口中的方法作为普通类方法不是更容易吗?使用接口的好处是什么?

我尝试在Google上寻找答案。有很多,但我仍然无法理解它的意义。我还阅读了这个问题和它的答案,但整个契约的概念使它变得更加复杂...

希望有人能够简化它! :)
提前感谢!


6
这个问题以前被问过很多次!但是你比大多数人都要礼貌得多地提出了这个问题!恭喜! - Ry-
1
你可以尝试查看Java词汇表以获取接口信息...http://mindprod.com/jgloss/interface.html - user1928746
也许一个更好的问题应该是,你可以具体说明一下你对已经阅读的教程和帖子有什么特别困惑的地方。这样我们就不会看到同样的东西被重复问了,你也能得到更好的帮助。 - Hovercraft Full Of Eels
https://dev59.com/0HVD5IYBdhLWcg3wTZ1m#24436493 - Jo Smo
9个回答

12

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

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

认为一些代码可能会有所帮助。这并不能解释所有关于接口的内容,请继续阅读,但我希望这可以让您开始了解。重点是您可以改变实现方式...

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. 

}

}

所以基本上,每个变量都是从接口类型(IExampleService)中声明的,在这种情况下,必须/包括接口中声明的方法,对吗?如果是这样,那么接口的使用就是“汇集”所有具有共同方法的类,对吗?此外,接口变量可以“包含”每个对象还是只能包含实现它的对象? - Asaf
每个实现接口的对象都实现了接口上的所有方法。将其视为合同。接口不会将类聚集在一起。我认为您无法从接口变量中访问对象数据,但接口变量将提供对接口的访问,无论对象是什么... 清楚吗? - 0909EM

11

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

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

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

现在假设您在应用程序中使用两个数据库,具体是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();

或者您可以使用 Factory/Abstract Factory 或 JS 或 Ruby 脚本引擎。


非常出色的接口实现,非常感谢。 - Abdulfattah Alhazmi
为什么不这样写呢:if (args[2].equals("mysql")) MySQL db = new MySQL(); else PostgreSQL db = new PostgreSQL(); 这种写法的意图不够明确。 - user1451111

4

它们用于实现多态性,而无需使用继承。


1
啊,但它们确实是继承的一种形式。也许你的意思是没有类继承? - Hovercraft Full Of Eels
“类继承”是唯一重要的继承形式,其他都是无用功。 - Ignacio Vazquez-Abrams
@HovercraftFullOfEels 我知道接口之间的继承,但在我的术语中,一个类实现一个接口,而不是从它继承。两者都是子类型化的形式,但这又是另一个概念。 - user395760
1
我恭敬地不同意。接口代表纯粹的“类型”,而接口继承则代表“类型”继承。这是非常重要、强大且实用的东西。 - Hovercraft Full Of Eels

3
接口的思想在于规定了一个类可以实现的契约。这个概念在你所阅读的其他帖子中已经被广泛涵盖。
直接实现接口中的方法是不够的原因在于,其他方法可以使用接口类型来要求这些方法存在。
让我们以AutoCloseable为例。它唯一的方法是close方法,但使用该类型表达“我需要一个可以关闭的参数”的想法比列出所有方法更容易:
public void passMeSomethingThatICanClose(AutoCloseable bar) {
    //stuff goes here
    bar.close(); //the compiler knows this call is possible
}

如果没有简洁的类型名称,该方法将不得不显式列出要求,这将不太容易,特别是当接口“契约”有许多方法时。
同样地,通过具有特定关键字“implements AutoCloseable”来表示意图,编译器可以告诉您是否正确实现了契约。
public class BrokenCloseable implements AutoCloseable {
    public void colse() {  //ERROR — see the typo?
        //blah
    }    

}

这是一个错误,因为方法名称不正确。在Java中,编译器可以(并且会)告诉你这一点。但是,如果你只负责实现方法本身,你将不会收到错误信息。相反,你的类将会静默地“不是”可自动关闭的。

其他一些语言(Python是一个很好的例子)不会像Java那样处理 —— 相反,它们会按照你在问题中建议的方式处理。这被称为“鸭子类型”(如果它看起来像鸭子,叫起来像鸭子,那就把它当作鸭子对待),这也是一种可行的方法,只是与Java不同。


2

接口规定了功能。从某种意义上说,它们提供了多继承。假设我有一个函数,用于执行列表操作。但并不仅限于列表,可以是任何集合类型。只要可以遍历其中的内容,就可以使用 Iterable 接口。

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

现在,这个功能可以与列表和哈希集合等完全不同的实现方式并属于不同的类结构一起使用,但是所有这些都提供了基本功能。

这与一个大型游戏循环不同,它会更新所有扩展公共类的实体,尽管可以使用接口。


2
List<T>接口来说,你只需要在一个地方决定是否要在特定场合使用ArrayList<T>或者LinkedList<T>(或者其他相关的类)。但是它们都实现了List<T>接口。因此,无论它们如何工作,甚至是否在层次结构上相关,它们都保证公开一组方法供你使用,你不必在代码的每个点上都知道你最终得到的集合对象背后的实现。

2
由于存在“致命死亡的钻石”,让我们考虑以下情况:
您在编程语言中编写了一个允许多重继承的类(名称:Parent)。该类包含一个实例变量(int a;)和一个方法(void hello())。然后,您创建了两个类(Kid1和Kid2)。每个类都扩展了Parent类。然后,在Kid1和Kid2类中覆盖hello()方法,并在Kid1和Kid2类中为a变量分配一些值。在最后一步中,您创建了第四个类(例如Main),它扩展了Kid1和Kid2类(多重继承)。现在...问题是,Main类将继承哪个hello()方法以及哪个a变量的值。
由于Java中不存在多重继承,我们有接口...抽象类包含抽象方法。我们可以实现多个接口,但前提是我们必须覆盖实现接口的所有方法。

1

1

你的问题太过宽泛,无法在单个帖子中回答而不会变得过于牵强或过于普遍而没有用处。因此,我将尝试给出一个有意义的例子,说明接口何时非常有用。

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

这时就需要用到接口。此时,你可以让所有的类都实现一个接口,比如Comparable,该接口指定任何实现它的类都将实现一个Compare方法。

现在,你的排序对象函数编写起来将变得非常简单,因为它只需依赖于任何需要比较的对象都具有相同的Comapre方法即可。

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


所以基本上,你的意思是接口被用来向所有Comparable对象“添加”一种共同方法?(显然开发人员首先必须从接口中实现compare方法)。如何检查一个对象是否实现了一个接口?我还经常看到这个用法,当引用接口时 someInterface a = new someClass(); ? 我部分理解它的作用,但如果函数在类本身中已经实现,为什么还需要它呢?顺便说一下,感谢您的回答,我最好理解它了! :) - Asaf
@xTCx - 为了测试一个对象是否实现了某个接口(或者是某种类型),你可以使用 instanceOf 运算符 - 在网上阅读更多相关信息。我很高兴你觉得我的示例有用! - Mike Dinescu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接