为了账号安全,请及时绑定邮箱和手机立即绑定

用于哈希集或其他实现的 Java 变量类型集合?

用于哈希集或其他实现的 Java 变量类型集合?

偶然的你 2022-09-28 15:59:01
我经常在类中看到像字段这样的声明。对我来说,使用变量类型的接口来提供实现的灵活性是完全有意义的。上面的示例仍然定义了必须使用哪种s,分别允许哪些操作以及在某些情况下(由于文档)它应该如何表现。List<String> list = new ArrayList<>();Set<String> set = new HashSet<>();Collection现在考虑这样一种情况,即实际上只需要(甚至)接口的功能才能在类中使用字段,并且这种实际上并不重要,或者我不想过度指定它。因此,我选择例如作为实现,并将字段声明为 。CollectionIterableCollectionHashSetCollection<String> collection = new HashSet<>();在这种情况下,该字段是否应实际属于类型?这种声明是不是不好的做法,如果是,为什么?或者,尽可能少地指定实际类型(并且仍然提供所有必需的方法)是一种很好的做法。我问这个问题的原因是因为我几乎从未见过这样的声明,最近我只需要指定接口的功能,我得到了更多。SetCollection例:// Only need Collection features, but decided to use a LinkedListprivate final Collection<Listener> registeredListeners = new LinkedList<>();public void init() {    ExampleListener listener = new ExampleListener();    registerListenerSomewhere(listener);    registeredListeners.add(listener);    listener = new ExampleListener();    registerListenerSomewhere(listener);    registeredListeners.add(listener);}public void reset() {    for (Listener listener : registeredListeners) {        unregisterListenerSomewhere(listener);    }    registeredListeners.clear();}
查看完整描述

3 回答

?
手掌心

TA贡献1942条经验 获得超3个赞

由于您的示例使用私有字段,因此隐藏实现类型并不重要。您(或维护此类的任何人)始终可以查看字段的初始值设定项以查看它是什么。

但是,根据它的使用方式,可能值得为该字段声明一个更具体的接口。将其声明为 a 表示允许重复,并且排序很重要。将其声明为 a 表示不允许重复,并且排序并不重要。您甚至可以将字段声明为具有特定的实现类(如果其中有重要内容)。例如,将其声明为 “表示不允许重复项,但排序很重要ListSetLinkedHashSet

如果类型出现在类的公共 API 中,以及该类的兼容性约束是什么,那么是否使用接口以及使用哪个接口的选择将变得更加重要。例如,假设有一个方法

public ??? getRegisteredListeners() { 
   return ...
}

现在,返回类型的选择会影响其他类。如果您可以更改所有呼叫者,也许这没什么大不了的,您只需要编辑其他文件即可。但假设调用方是一个您无法控制的应用程序。现在,接口的选择至关重要,因为您无法在不破坏应用程序的情况下更改它。这里的规则通常是选择最抽象的接口,以支持您希望调用方想要执行的操作。

大多数 Java SE API 返回 。这提供了从底层实现的相当程度的抽象,但它也为调用方提供了一组合理的操作。调用方可以迭代、获取大小、执行包含检查或将所有元素复制到另一个集合。Collection

某些代码库用作要返回的最抽象的接口。它所做的只是允许调用方进行迭代。有时这是所有必要的,但与 相比,它可能有些限制。IterableCollection

另一种选择是返回 .如果您认为调用方可能希望使用流的操作(如筛选、映射、查找等),而不是迭代或使用集合操作,这将非常有用。Stream

请注意,如果选择返回 或 ,则需要确保返回不可修改的视图或创建防御性副本。否则,调用方可能会修改类的内部数据,这可能会导致 bug。(是的,即使是可以修改!请考虑获取 一个,然后调用该方法。如果返回 ,则无需担心,因为不能使用 来修改基础源。CollectionIterableIterableIteratorremove()StreamStream

请注意,我将有关字段声明的问题转换为有关方法返回类型声明的问题。有一种“编程到接口”的想法在Java中非常普遍。在我看来,对于局部变量来说,这并不重要(这就是为什么它通常可以使用),对于私有字段来说也无关紧要,因为这些(几乎)根据定义只影响它们被声明的类。但是,“程序到接口”原则对于API签名非常重要,因此在这些情况下,您确实需要考虑接口类型。私人领域,不是那么多。var

(最后一点:有一种情况是,你需要关注私有字段的类型,那就是当你使用一个直接操作私有字段的反射框架时。在这种情况下,您需要将这些字段视为公共字段 - 就像方法返回类型一样 - 即使它们未声明。public


查看完整回答
反对 回复 2022-09-28
?
慕的地6264312

TA贡献1817条经验 获得超6个赞

与所有事情一样,这是一个权衡问题。有两种对立的力量。

  • 类型越通用,实现的自由度就越高。如果您使用,则可以自由使用 、或,而不会影响用户/调用方。CollectionArrayListHashSetLinkedList

  • 返回类型越通用,用户/调用方可用的功能就越少。提供基于索引的查找。A 使得通过 、 和 获取连续子集变得容易。A 提供了高效的 O(log n) 二进制搜索查找方法。如果您返回 ,则这些都不可用。只能使用最通用的访问函数。ListSortedSetheadSettailSetsubSetNavigableSetCollection

此外,子类型保证特殊属性不:s 保存唯一项。已排序。有订单;它们不是无序的物品袋。如果使用,则用户/调用方不一定假定这些属性成立。他们可能被迫进行防御性编码,例如,即使您知道不会有重复项,也可以处理重复项。CollectionSetSortedSetListCollection

合理的决策过程可能是:

  1. 如果 O(1) 索引访问得到保证,请使用 。List

  2. 如果元素已排序且唯一,请使用 或 。SortedSetNavigableSet

  3. 如果元素唯一性得到保证,但顺序没有保证,请使用 。Set

  4. 否则,请使用 。Collection


查看完整回答
反对 回复 2022-09-28
?
噜噜哒

TA贡献1784条经验 获得超7个赞

这实际上取决于您要对集合对象执行的操作。

Collection<String> cSet = new HashSet<>();
Collection<String> cList = new ArrayList<>();

在这种情况下,如果你愿意,你可以做:

cSet = cList;

但如果你喜欢:

Set<String> cSet = new HashSet<>();

尽管您可以使用构造函数构造新列表,但不允许执行上述操作。

 Set<String> set = new HashSet<>();
 List<String> list = new ArrayList<>();
 list = new ArrayList<>(set);

所以基本上取决于你可以使用的用法或接口。CollectionSet


查看完整回答
反对 回复 2022-09-28
  • 3 回答
  • 0 关注
  • 84 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信