例如:
List<String> list = new ArrayList<String>();
VS
ArrayList<String> list = new ArrayList<String>();
这两者之间的确切区别是什么?
我们何时应该使用第一个?何时应该使用第二个?
答案 0 :(得分:5)
尽可能使用第一种形式(我甚至会说:如果足够的话,使用Collection
)。当接受来自客户端代码(方法参数)的输入时,这尤其重要。有时,为了方便客户端代码/库用户,最好接受最通用的输入(如Collection
)并处理它而不是强迫用户一直转换参数(用户有{ {1}}但API需要LinkedList
- 太糟糕了。)
仅当您需要调用ArrayList
中定义但list
中未定义的ArrayList
变量上的方法时才使用第二种形式(如ArrayList.trimToSize()
)。此外,当向用户返回数据时,请考虑(但这不是经验法则)返回更具体的类型。例如。考虑List
超过List
,以便客户端代码可以更轻松地处理结果。然而!返回过于具体的类型(例如Collection
)将锁定您未来的实施,因此请尝试找到妥协方案。
这是一般规则 - 使用最常用的类型。更为一般:使用常识。
答案 1 :(得分:3)
List不是超类,它是一个接口。 通过使用List而不是ArrayList,可以确保列表的用户只使用List上定义的方法。这意味着您可以将实现更改为(例如)Vector,而不会破坏现有代码。
所以,请使用第一种形式。
答案 2 :(得分:1)
第一种形式是最理想的形式,因为您从代码的其余部分隐藏了实现(ArrayList),并确保您的代码仅适用于抽象(List)。这样做的好处是您的代码将更通用,因此更容易适应,例如当您从使用ArrayList更改为LinkedList,Vector或自己的List实现时。它还意味着本地更改不太可能导致代码其他部分发生更改(“涟漪效应”),从而提高代码的可维护性。
当您想要使用List界面未提供的变量时,您需要第二个表单,例如ensureCapacity或trimToSize
编辑:更改实施的额外说明
以下是将变量声明为Collection(java.util中更通用的接口)的示例:
public class Example {
private Collection<String> greetings = new ArrayList<String>();
public void addGreeting(String greeting) {
greetings.add(greeting);
}
}
现在假设您要更改实施以存储唯一的问候语,因此从ArrayList切换到HashSet。两者都是Collection接口的实现。在这种情况下,这很容易,因为所有现有代码都将问候字段视为集合:
public class Example {
private Collection<String> greetings = new HashSet<String>();
public void addGreeting(String greeting) {
greetings.add(greeting);
}
}
有一个例外。如果有代码将问候字段强制转换为其实现,则会使该代码“实现感知”,违反了您尝试实现的信息隐藏,例如:
ArrayList<String> greetingList = (ArrayList<String>) greetings;
greetingList.ensureCapacity(42);
如果将实现更改为HashSet,此类代码将导致运行时错误“java.lang.ClassCastException:java.util.HashSet与java.util.ArrayList不兼容”,因此应尽可能避免这种做法。
答案 3 :(得分:0)
对具体类使用接口有一些优点:
只有在使用它的某些功能时才需要具体实现。 例如。我们有Matrix接口,并有两个具体的实现SparseMathix和FullMatrix。如果你想有效地将它们相乘,你可以使用SparseMatrix的一些实现细节,否则性能可能太慢。