关于 Java 接口和多态性

Posted

技术标签:

【中文标题】关于 Java 接口和多态性【英文标题】:About the Java interface and polymorphism 【发布时间】:2013-04-07 14:24:39 【问题描述】:

我在阅读 Java 文档时遇到了一个奇怪的案例。这是关于 Arrays.asList 方法的 Oracle 的 java 文档的链接,http://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#asList(T...)

文档中有一个例子

List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");

我的问题是,由于 List 是一个接口,为什么我们可以将 stooges 声明为“List”,而不是实现 List 的具体子类(例如 ArrayList 或 LinkedList)? 那么这是否意味着我们可以有一个接口类型的引用变量呢?我觉得这很奇怪,因为我一直认为接口只代表多态性,我们永远不应该真正使用接口类型变量。

谁能给我一些线索?

【问题讨论】:

we should never really use a interface type variable 完全相反是正确的!!! 我知道对于多态性,我们应该将函数参数和返回类型和数组类型声明为接口类型变量。但是对于我在问题中提到的情况,我很困惑.. java中的局部变量、函数参数和返回类型几乎没有区别。能够将对象视为接口是接口的主要特征之一。 @OsamaJaved - 小幅更正:“能够将对象视为接口的实例 ...” @HotLicks 不错 :D 【参考方案1】:

将 List 接口视为一种保证。任何实现 List 的类都将保证具有接口的方法。当 Arrays.asList() 返回一个 List 时,您实际上并没有获得接口,您获得的是一个保证实现 List 接口中列出的方法的具体类

至于您的“我们永远不应该真正使用接口类型变量”,您实际上应该这样做。它被称为“接口编程”。如果您可以返回一个列表而不是像 LinkedList 之类的东西,那么它会更加灵活。您的方法的调用者不耦合到您可能使用并返回 LinkedList 的特定实现内部实现。如果在某些时候您想返回一个 ArrayList 而不是 LinkedList,则调用者不必更改任何代码,因为他们只关心接口。

What does it mean to "program to an interface"?

请注意,Serializable 是一个标记接口,因此有点奇怪。它不保证方法存在,而是保证实现可序列化的类的创建者已经考虑了与序列化类相关的许多问题(覆盖 readObject/writeObject、与其他序列化形式的兼容性以及其他问题 @987654322 @)。所以 Serializable 仍然提供保证,就像 List 一样,但它不是关于方法签名,而是关于语言的语言外特性。

http://en.wikipedia.org/wiki/Marker_interface_pattern

【讨论】:

【参考方案2】:

使用接口作为引用类型在 Java 中是一种完全有效的做法。例如,Serializable 接口将在其类中执行此操作,以便传递给它的任何对象都可以序列化。

这也是 Java 提供类似于 Multiple Inheritance 的方式的方式。例如:

public interface A  
public class B implements A 

public class program 
     B bClass = new B();
     A aObject = (A)bClass;

这样可以用不同的引用类型来引用同一个对象,而且不会弄乱继承链!

【讨论】:

【参考方案3】:

接口为实现定义了contractspecification。这是方法及其签名。所以实现接口的类必须尊重contract。这样您就可以在不影响使用接口声明变量的代码的情况下更改实现。

在你提到的例子中:

    除非您查看代码,否则您不知道List 接口Arrays.asList 正在使用什么实现。那么你怎么知道使用哪一个呢? (请参阅 javadoc 了解列表接口以了解它有哪些实现)

    实现可能会发生变化,如果Arrays.asList 决定使用其他实现怎么办?您的代码将被破坏。

    方法 Arrays.asList 的签名是它返回 List&lt;T&gt; 所以如果你想有一个具体的实现作为变量,你将不得不强制转换返回值,这是不好的做法或创建新的 -假设ArrayList - 并将所有元素复制到其中,这只是不必要的开销。

【讨论】:

【参考方案4】:

Effective Java by Bloch 是一本关于 Java 最佳实践的好书。特别是,item #52 谈到了这一点:“如果存在适当的接口类型......使用接口类型声明。”

一般概念是,为了获得最大的灵活性和可理解性,您应该使用最能反映上下文的类型,通常是接口。在您提供的示例中,确切的实现是否重要,或者只是它是一个列表。当然,如果代码需要特定于 ArrayList 的方法,或者如果代码依赖于特定于 ArrayList 的行为,则使用具体类。

偶尔会有例外,比如当using GWT-RPC,但这是出于实现原因。

【讨论】:

【参考方案5】:

这确实是多态能力的一个很好的例子,如果你喜欢你可以看看Arrays.asList()的源代码Arrays.asList(T...a),你会发现它需要可变长度输入和定义了自己的私有静态具体类ArrayList,它实现了List接口,而不是使用众所周知的java.util.ArrayList或其他java 集合类型, 这可能是为了提高效率或其他原因,您想实现自己的类并将其返回给用户,而不会被实现细节压倒,因为他可以通过一个接口来处理您的私有类。

【讨论】:

以上是关于关于 Java 接口和多态性的主要内容,如果未能解决你的问题,请参考以下文章

关于Java的特点之多态

关于Java多态

设计模式中的多态——策略模式详解

JAVA 作业 实验名称:接口,继承与多态

Java多态&接口&转型

Java多态&接口&转型