3

我在阅读 Java 文档时遇到了一个奇怪的案例。这是 Oracle 的关于 Arrays.asList 方法的 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)?那么这是否意味着我们可以有一个接口类型的引用变量呢?对我来说这看起来很奇怪,因为我一直认为接口只代表多态性,我们永远不应该真正使用接口类型变量。

谁能给我一些线索?

4

5 回答 5

3

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

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

“编程到接口”是什么意思?

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

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

于 2013-04-07T14:31:28.337 回答
1

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

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

public interface A { }
public class B implements A {}

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

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

于 2013-04-07T14:28:47.120 回答
0

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

在您提到的示例中:

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

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

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

于 2013-04-07T14:38:35.397 回答
0

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

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

偶尔会有例外,例如在使用 GWT-RPC时,但这是出于实现原因。

于 2013-04-07T14:51:32.760 回答
0

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

于 2013-04-07T14:56:40.860 回答