jjzjj

HashSet 或其他实现的 Java 变量类型集合?

coder 2024-03-11 原文

我经常看到类似 List<String> list = new ArrayList<>(); 的声明或 Set<String> set = new HashSet<>();对于类中的字段。对我来说,使用变量类型的接口(interface)来提供实现的灵 active 是非常有意义的。上面的例子仍然定义了哪种 Collection必须分别使用 s ,分别允许哪些操作以及在某些情况下它应该如何表现(由于文档)。

现在考虑实际上只有 Collection 的功能的情况。 (甚至 Iterable )接口(interface)需要使用类中的字段和 Collection 的种类实际上并不重要,或者我不想过度指定它。所以我选择例如HashSet作为实现并将该字段声明为 Collection<String> collection = new HashSet<>(); .

那么该字段实际上应该是 Set 类型吗?在这种情况下?这种声明是不是不好的做法,如果是,为什么?或者尽可能少地指定实际类型(并仍然提供所有必需的方法)是一种很好的做法。我问这个的原因是因为我几乎没有见过这样的声明,最近我在我只需要指定 Collection 的功能的情况下得到了更多。界面。

例子:

// Only need Collection features, but decided to use a LinkedList
private 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();
}

最佳答案

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

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

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

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

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

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

一些代码库使用 Iterable作为最抽象的接口(interface)返回。它所做的只是允许调用者进行迭代。有时这就是全部必要的,但与 Collection 相比可能有些限制。 .

另一种选择是返回 Stream .如果您认为调用者可能想要使用流的操作(例如过滤器、映射、查找等)而不是迭代或使用集合操作,这将很有帮助。

请注意,如果您选择返回 CollectionIterable ,您需要确保返回不可修改的 View 或制作防御性副本。否则,调用者可能会修改您的类的内部数据,这可能会导致错误。 (是的,即使是 Iterable 也可以允许修改!考虑获取一个 Iterator 然后调用 remove() 方法。)如果你返回一个 Stream ,您无需担心,因为您不能使用 Stream修改底层源。

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

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

关于HashSet 或其他实现的 Java 变量类型集合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55601504/

有关HashSet 或其他实现的 Java 变量类型集合?的更多相关文章

  1. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  2. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  3. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  4. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  5. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  6. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  7. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  8. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  9. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  10. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

随机推荐