什么时候应该在java中使用接口?
一个很好的例子是什么时候在Java中准确地使用接口将是理想的,任何适用的具体裁决。
使用接口来定义“第三方”供应商必须完全遵守和实施的应用程序编程合同(蓝图,接口)。 通过这种方式,最终用户只需对API合同进行编码,并轻松切换具体实现“底层”,而无需更改代码。
JDBC API就是一个很好的例子。 它几乎只存在接口。 具体实现作为“JDBC驱动程序”提供。 这使您可以独立于数据库(DB)供应商编写所有的JDBC代码。 只要您希望切换数据库供应商,就可以在不更改任何Java代码行(除了任何硬编码的特定于数据库的SQL代码之外)的情况下更改JDBC驱动程序。
另一个例子是Java EE API ,它也包含非常多的接口和抽象类。 具体实现提供为“Java EE应用服务器”,“Servlet容器”等,如Sun Glassfish,Apache Tomcat等。这使得您可以将Web应用程序(WAR)部署到您喜欢的任何Java Web服务器。
collections框架是一个很好的地方。
java.util.List //interface java.util.ArrayList //Concrete class java.util.LinkedList //Concrete class
所以你可以写这样的代码:
List l = new ArrayList(); l.add(..) //do something else.
如果将来你想用LinkedList
来更改实现,或者你自己AwesomeList which implements List
接口的AwesomeList which implements List
,那么你所要做的就是将第一行改为:
List l = new MyAwesomeList(); or List l = new LinkedList();
其余的代码将贯穿始终。
需要接口的地方,你希望你的程序出现波动,你期望的变化点,你的devise需要弯曲的地方。
在这个意义上,实现是脆弱的:它很容易中断。 这就是为什么子类化并不总是最好的解决scheme,正如冗长的方法自己实现一些复杂的行为通常是一个坏主意。
接口比实现更灵活,可以处理更多的程序devise压力。
通过在你的程序中引入接口,你真的引入了变化点,你可以插入不同的接口实现。 接口的主要目的是抽象 ,把“什么”与“如何”分开。
为了安全起见 ,一个重要的规则是Liskov替代原则 [ UncleBob , Wikipedia ]。 尽pipe像Java这样的语言中的编译器会确保语法上所有内容都是正确的(参数,types等等),但LSP处理语义 。 简而言之,LSP表示每个接口的实现必须(也)正确地performance自己,以便如上所述真正可以替代。
尝试了解战略devise模式
在需要多个相同行为的实现时使用接口。 下面是一个对象可以实现的接口示例,以显示它们都可以序列化为XML。
public interface Xmlizable { public String toXML(); }
那么你可以将“Xmlizable”接口传递给只关心那个接口的方法。
在链接文本中查看JDK集合教程。 考虑collections。 你在想什么? 可以订购,也可以不订购,也可能有重复。
所以Collection是一个List(有序)和Set(无序)作为子接口的接口。 现在列表中有很多问题,应该是同步还是不同步,是否应该是链表。每个“行为”都有自己的接口/抽象类。
当您想要在集合中指定“某些”行为时,需要抽象类。 例如,所有的集合(集合/列表等)都可以有一个“toString”表示,它只是迭代元素(sorting/不sorting)并将它们串联起来。 那个行为可以出现在“AbstractCollection”等
如果您遵循JDK集合的层次结构,那么它是了解接口和抽象类的好地方:)
基本上,当你需要“省略”一些实现细节时,你可以在接口和抽象类之间进行select。 接口通常是更好的select,因为客户端类可以实现任意数量的接口,但是他们只能有一个超类(“inheritance是稀缺资源”,就像他们所说的那样)。
为什么你想要一个抽象类或一个接口? 因为有时候,当你写一个algorithm的时候,你并不关心它的一个特定的子步骤是如何完成的,而只是根据某种契约完成的。 一个例子是Collections API, List是一个接口 – 通常,当你使用List
,你并不在意它是否把东西保存在一个数组或者节点的链表中或者其他types的办法。 只要它存储你放在那里的东西,你就很高兴。
然后,我们有AbstractList
:一个实现List
的抽象类,它提供了几乎所有的List
实现的必要 – 创build自己的List
实现,所有你需要做的就是扩展AbstractList
并填充一些方法。 当抽象类是一个很好的select时,这是一个很好的例子 – 当你想要提供一个几乎完整的东西的实现时,只需要填补客户代码缺乏的一些空白。
提示:如果你创build一个只包含抽象方法的抽象类,你应该可以创build一个接口。
从oracle文档页面
考虑使用接口如果:
- 你期望不相关的类将实现你的接口。 例如,许多不相关的对象可以实现Serializable接口。
- 您想要指定特定数据types的行为,但不关心谁实现其行为。
- 你想利用types的多重inheritance。
看看相关的SE问题已经有很好的答案的代码示例。
有更多的接口比正确的方法
界面和抽象类有什么区别?
我应该如何解释一个接口和一个抽象类的区别?
OOP的一个基本原理是信息隐藏:隐藏实现细节,只给调用者显示基本服务的描述。
Java必须为此构build目标:接口和抽象类。 你可以定义一个接口,编写你的代码,通过调用其中定义的“可用方法”,它只依赖于接口。
信息隐藏既可用于读取外部类(在某种意义上外部来自您正在编写的模块之外),这样您就可以定义您需要的方法,而且不需要推断其具体的实现types,或者何时定义一个可以在你的类之外使用的数据types – 一个典型的例子就是例如Collection API或J2EE,就像其他人已经提到的那样。
接口和抽象类都提供了这些细节 – 但有两个主要区别:接口支持多inheritance,而抽象类可以持有基本的实现。 为了最大限度地提高效率,在定义接口时,还应该定义一个有意义的默认实现的抽象类。 如果接口的用户不需要扩展任何其他类,则可以扩展此抽象类,否则需要从接口实现所有方法。 另一方面,不要直接读这个抽象类,接口应该是抽象的。
远程通讯中的接口:
接口也可以用来定义一个商定的“协议”,用于系统不同部分之间的通信,可能通过远程调用。 所以接口只定义了可以调用什么参数,以及在调用之后返回什么。 客户端使用接口,服务器端实现具体的实际代码。
边注:
在Java中,只能从一个类中固有(扩展),但是可以实现多个接口,所以当您需要这种多重inheritance以及决定不使用复合inheritance时,有时您将不得不使用接口。
这个答案与coolest_head的基本相同,只是在传达实用性方面更加明确。
正如coolest_head所解释的那样,当将来可能要切换程序的可能子组件时,接口是非常有用的。 它们还允许您更轻松地分离程序结构各个部分的问题,因为使用接口可以确保某些非相关的类等对程序的其他部分不可见。
作为一个例子,假设你想读取任意数据并打印它,如下所示:
SomeReader someReader = new SomeReader(); String data = someReader.readLine(); System.out.println(data);
这里没什么特别的,对吗? 但是,虽然这个例子很简单,但它已经和SomeReader
类绑定了,这意味着你对这个类SomeReader
所有更改都必须传播到你正在使用这个类的类上 – 尤其是如果你重构了一些内部的部分! 相反,你想这样做
IMyReader reader = new SomeReader(); System.out.println(reader.readLine());
你几乎在那里 – 现在的打印代码不再关心具体的实现,只是关于接口暴露的部分。 这通常是足够的,因为现在你可以转换一个new
语句,并获得新的实现,以及任何仍然按预期工作的任何东西( 只要接口的合同在实现类中受到尊重! )。 当你最终使用一个特定的对象多次时,这是特别方便的 – 在这里我只用了一次,但是真的,如果你正在使用例子列表,通常做多less个操作同一个列表?
所以,为了真正打破这个例子,这里是你的代码可能最终看起来像
public class RowPrinter { private final IMyReader reader; public RowPrinter(IMyReader reader) { this.reader = reader; } public void print() { IMyReader reader = getReader(); System.out.println(reader.readLine()); } protected IMyReader getReader() { return reader; } }
注意与构造函数的部分? 这是控制的倒置,让我告诉你,那是一个很酷的软件工程。 我可以从经验上讲,它可以帮助你很多麻烦,无论是从数据库产品切换到另一个,或使某些代码线程安全。 或者,也许你只是想添加一层日志logging到一些类,很容易使用包装装饰器 ,恰好实现与包装类相同的接口。 而这仅仅是个开始。
接口带来很多好处,通常从简单的例子中就不那么明显了,尽pipe简单的例子可以帮助你正确地使用。 虽然Java中的接口是一种语言结构,但实际上它们更像是一种编程范式,而不仅仅是一种单一语言的function,在某些语言中,仿真接口确实是有益的,只要找出正确的方法就可以了。
每当您计划在某个开发阶段用另一个类replace实现类时,请使用接口。
至less在更严重的项目中,我也会build议使用接口后端来处理inheritance关系中的所有类:不幸的是我没有链接,但是Java语言的开发者曾经说过,包括类inheritance是最大的错误devise语言。
参数非常好:使用适当的devise,总是可以通过接口inheritance来replace类inheritance,并且您可以获得很多代码维护。 保留自然types关系(比如像几何(“正方形”是一个矩形),比使用类inheritance更容易。