类实例化 - 变量的抽象类或具体类

时间:2013-05-09 15:52:02

标签: java abstract-class

我今天的问题不是问题解决问题,而是最佳实践理论问题。我熟悉了Joda-Time,我在用户指南(http://joda-time.sourceforge.net/userguide.html#Interface_usage)中看到了这一点:

  

使用Collections接口(例如List或Map)时,通常会将变量保存为List或Map类型,仅在创建对象时引用具体类。

 List list = new ArrayList();
 Map map = new HashMap();

这让我感到非常奇怪。在我的编程中,除非我编写某种类型的API,否则我总是为变量保留具体的类。有没有人有任何有用的材料来解释为什么优先保留通用/抽象类与变量类型的具体类的原因?

为了说明,我写了一个简单的方法,每当我需要一个对象列表作为由逗号分隔的字符串时,我就会使用它:

public static <T> String CSV(Collection<T> list) {
    boolean first = true;
    StringBuilder sb = new StringBuilder();
    for(Object e : list) {
        if(first)
            first = false;
        else
            sb.append(", ");
        sb.append(e.toString());
    }
    return sb.toString();
}

当然,在这里,你必须使用Collection,这样你就可以传入任何你想要的东西,Object [],LinkedList等。

但是,让我们说别处我存储了一组字符串,我希望它是一个LinkedHashSet,我会像这样创建变量:

LinkedHashSet<String> set = new LinkedHashSet<String>();

LinkedHashSet<String> set = new LinkedHashSet<>();

(因为Java 7中的变化)

但根据Joda-Time的用户指南,我应该这样做:

Set<String> set = new LinkedHashSet<String>();

我只是没有看到优点。任何人都有有用的输入/阅读材料?

谢谢!

5 个答案:

答案 0 :(得分:2)

如果您使用Collection(例如)并不重要,那么Collection如何实现 - 您想要使用Collection方法而已。 在这种情况下,您的代码非常敏捷,您可以轻松更改Collection实现。但如果基本界面不够 - 请使用具体参考。

这是OOP基础知识 - 如果可能的话,你应该使用抽象,而不是具体的类。

类似的问题:

答案 1 :(得分:0)

由于Java尚未进行类型推断,因此您必须在定义变量时显式指定变量,并且每次基于它创建新变量时都必须。如果类型是具体类,这意味着如果您以后想要更改它,则必须通过并更改所有这些声明。重构工具可能会使其自动化,但它仍然是一个小麻烦。使用抽象或接口类型允许您更改具体类型而不更改所有这些声明。

总的来说,无论如何,我认为这不是什么大问题。

答案 2 :(得分:0)

如果您正在编写一些将被其他人使用或修改的代码,那么使用界面并让他们选择最佳实现更容易,而不是选择具体类型,从而迫使他们使用您的类型。

答案 3 :(得分:0)

您通常希望使用最接近定义所需功能的界面。

以下是在LinkedList上使用Queue的一些原因:

  • Queue告诉读者如何使用变量 - 它更具描述性。
  • 您可以将对象的实现替换为立即实现Queue的任何内容。
  • 如果你使用DI,你甚至不需要知道你的队列的实现,并且可以在以后修改它而不用修改代码。
  • 如果您只使用Queue方法,则使用LinkedList over Queue没有任何优势。
  • 编译器可断言您的队列未被不当使用。

由于多种原因,最后一个是非常重要的一点。如果你编写一个返回“队列”的方法,你可以预期它不会被不适当地处理,你可以立即向用户描述他们的新对象是如何被操纵的。例如,它不打算作为List进行迭代。

这也适用于您自己的接口,您可能希望创建一个接口来为一个目的公开一个功能子集,同时为另一个目的公开一个不同的子集...例如,您可以将相同的队列返回到两个不同的调用者,一个带有“Enqueue”界面,一个带有“Dequeue”界面 - 使得“管道”应该流向哪个方向非常清楚。

答案 4 :(得分:0)

我对实例化的一般经验法则是使用最符合您需求的基类。

我的意思是,在这种情况下,你使用的是LinkedHashSet,如果你知道你只是要使用Set的契约所实现的方法,那么将它实例化为一个Set。这样做的原因是它使您的代码更具前瞻性。例如,如果您需要返回并确保Set是线程安全的,则实例化Concurrent类而不是非线程安全的LinkedHashSet。这样,您的下游代码不会更改,您只需要更新实例化。