【问题标题】:Interfaces in Java - what are they for? [duplicate]Java 中的接口——它们有什么用? [复制]
【发布时间】:2012-12-11 14:56:07
【问题描述】:

可能重复:
The purpose of interfaces continued

我不久前才开始学习 Java。
我遇到过Interfaces,我知道如何使用它,但仍然不能完全掌握它的概念。
据我了解,interfaces 通常由类实现,然后必须实现接口中声明的方法。
问题是——究竟有什么意义?将接口中的方法实现为普通的类方法不是更容易吗?使用接口究竟有什么好处?

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

希望有人可以将其简化得足够好! :)
提前致谢!

【问题讨论】:

  • 这已经被问了很多了!但是你问的比他们大多数人都礼貌得多!恭喜!
  • 您可以尝试使用 Java 词汇表获取接口信息 .... mindprod.com/jgloss/interface.html
  • 虽然也许一个更好的问题是您可以描述具体使您对您阅读的教程和帖子感到困惑的地方。这样我们就不会看到对相同旧事物的重新散列,并且您会获得更好的帮助。

标签: java interface


【解决方案1】:

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

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

想出一些代码可能会有所帮助。这并不能解释接口的更多内容,请继续阅读,但我希望这能让您入门。这里的重点是你可以改变实现......

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) 的变量都必须/确实包含接口中声明的方法,对吧?如果是这样,这意味着接口的(一个)用法是“汇集”所有具有通用方法的类,对吗?此外,接口变量是否可以“包含”每个对象或仅包含实现它的对象?
  • 每个实现接口的对象都实现了接口上的所有方法。将其视为合同。接口不会将类聚集在一起。我不认为你可以从接口变量访问对象数据,但无论对象是什么,接口变量都将提供对接口的访问......像泥巴一样清楚吗?
  • 是的!谢谢-我现在明白了:)
【解决方案2】:

接口可以用于很多事情。最常见的用途是 polymorphismdependency 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
    }
}

现在根据您的客户偏好,您可以在 main 中实例化 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 的工厂/抽象工厂或脚本引擎。

【讨论】:

  • 非常优秀的接口实现,非常感谢。
  • 为什么不if (args[2].equals("mysql")) MySQL db = new MySQL(); else PostgreSQL db = new PostgreSQL(); 点不够清楚。
【解决方案3】:

它们用于实现polymorphism,而不需要使用继承。

【讨论】:

  • 啊,但它们一种继承形式。也许您的意思是没有类继承?
  • “类继承”是唯一重要的继承形式。其他的都是faff。
  • @HovercraftFullOfEels 我知道接口之间的继承,但在我的行话中,一个类实现一个接口,它不会从它继承。两者都是 subtyping 的形式,但这是另一个概念。
  • 我不同意。接口代表纯类型,接口继承代表类型继承。重要、强大且实用的东西。
【解决方案4】:

接口的想法是它们指定一个类可以实现的契约。这个想法在您阅读的另一篇文章中大部分都有介绍。

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

我们以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 不同。

【讨论】:

    【解决方案5】:

    接口指定功能。在某种程度上,它们提供了多重继承。假设我有一个对列表执行某些操作的函数。但不仅仅是一个列表,任何集合。我可以遍历内容的任何内容。所以我使用Iterable接口。

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

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

    【讨论】:

      【解决方案6】:

      List&lt;T&gt; 接口为例。您只能在某一时刻决定是否要在特定场合使用 ArrayList&lt;T&gt;LinkedList&lt;T&gt;(或其他相关内容)。

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

      【讨论】:

        【解决方案7】:

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

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

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

        【讨论】:

          【解决方案8】:

          我建议你看看 Strategy Pattern 和 Composition Pattern。

          【讨论】:

            【解决方案9】:

            您的问题是在单个帖子中广泛回答的方式,既不会太做作,也不会太笼统而没有用处。因此,我将尝试给出一个有意义的示例,说明接口何时非常有用。

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

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

            现在,您的命令对象的函数编写起来将变得微不足道,因为它可以依赖于它需要比较的任何对象类都具有相同的 Comapre 方法这一事实。

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

            【讨论】:

            • 所以基本上,你的意思是接口被用来“添加”某种通用方法到所有 Comparable 对象? (显然,开发人员首先必须从接口实现 compare 方法)。你将如何检查一个对象是否实现了一个接口?在提到接口someInterface a = new someClass(); 时,我也看到了很多这种用法?我部分了解了它的作用,但是如果该函数是在类本身中实现的,为什么还需要它呢?感谢您的回答,顺便说一句,我最了解它! :)
            • @xTCx - 要测试一个对象是否实现了某个接口(或属于某个类型),您可以使用 instanceOf 运算符 - 在 Web 上阅读有关它的更多信息。很高兴您发现我的示例很有用!
            猜你喜欢
            • 2012-01-05
            • 2013-06-23
            • 2023-03-24
            • 2011-05-25
            • 1970-01-01
            • 2011-03-15
            • 2014-05-18
            • 2019-03-02
            • 2012-04-28
            相关资源
            最近更新 更多