我今天的问题不是问题解决问题,而是最佳实践理论问题。我熟悉了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>();
我只是没有看到优点。任何人都有有用的输入/阅读材料?
谢谢!
答案 0 :(得分:2)
如果您使用Collection
(例如)并不重要,那么Collection
如何实现 - 您想要使用Collection
方法而已。
在这种情况下,您的代码非常敏捷,您可以轻松更改Collection
实现。但如果基本界面不够 - 请使用具体参考。
这是OOP基础知识 - 如果可能的话,你应该使用抽象,而不是具体的类。
答案 1 :(得分:0)
由于Java尚未进行类型推断,因此您必须在定义变量时显式指定变量,并且每次基于它创建新变量时都必须。如果类型是具体类,这意味着如果您以后想要更改它,则必须通过并更改所有这些声明。重构工具可能会使其自动化,但它仍然是一个小麻烦。使用抽象或接口类型允许您更改具体类型而不更改所有这些声明。
总的来说,无论如何,我认为这不是什么大问题。
答案 2 :(得分:0)
如果您正在编写一些将被其他人使用或修改的代码,那么使用界面并让他们选择最佳实现更容易,而不是选择具体类型,从而迫使他们使用您的类型。
答案 3 :(得分:0)
您通常希望使用最接近定义所需功能的界面。
以下是在LinkedList上使用Queue的一些原因:
由于多种原因,最后一个是非常重要的一点。如果你编写一个返回“队列”的方法,你可以预期它不会被不适当地处理,你可以立即向用户描述他们的新对象是如何被操纵的。例如,它不打算作为List进行迭代。
这也适用于您自己的接口,您可能希望创建一个接口来为一个目的公开一个功能子集,同时为另一个目的公开一个不同的子集...例如,您可以将相同的队列返回到两个不同的调用者,一个带有“Enqueue”界面,一个带有“Dequeue”界面 - 使得“管道”应该流向哪个方向非常清楚。
答案 4 :(得分:0)
我对实例化的一般经验法则是使用最符合您需求的基类。
我的意思是,在这种情况下,你使用的是LinkedHashSet,如果你知道你只是要使用Set的契约所实现的方法,那么将它实例化为一个Set。这样做的原因是它使您的代码更具前瞻性。例如,如果您需要返回并确保Set是线程安全的,则实例化Concurrent类而不是非线程安全的LinkedHashSet。这样,您的下游代码不会更改,您只需要更新实例化。