通过扩展超类和扩展自己的类来创建实例有什么区别?

时间:2011-10-05 10:59:40

标签: java

例如:

List<String> list = new ArrayList<String>();

VS

ArrayList<String> list = new ArrayList<String>();

这两者之间的确切区别是什么?

我们何时应该使用第一个?何时应该使用第二个?

4 个答案:

答案 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界面未提供的变量时,您需要第二个表单,例如ensureCapacitytrimToSize

编辑:更改实施的额外说明

以下是将变量声明为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)

对具体类使用接口有一些优点:

  1. 您不会遇到具体的实施(您可以轻松更改它而无需修改代码)
  2. 您的代码更清晰,因为没有具体类的方法可用
  3. 只有在使用它的某些功能时才需要具体实现。 例如。我们有Matrix接口,并有两个具体的实现SparseMathix和FullMatrix。如果你想有效地将​​它们相乘,你可以使用SparseMatrix的一些实现细节,否则性能可能太慢。