【问题标题】:Why are the 'Arrays' class' methods all static in Java?为什么“数组”类的方法在 Java 中都是静态的?
【发布时间】:2016-12-29 18:33:48
【问题描述】:

我在浏览 Java 文档时了解到,Java 中 Arrays 类中的方法都是静态的。我真的不明白他们将其设为静态的原因。

例如,下面的代码违反了OO方法,因为如果我有一个类型'X',那么所有作用于它的方法都应该在它里面:

int[] a = {34, 23, 12};
Arrays.sort(a);

如果他们实现了以下方式会更好:

int[] a = {34, 23, 12};
a.sort();

有人能解释一下吗?

【问题讨论】:

标签: java arrays static


【解决方案1】:

Arrays 类包含独立于状态的方法,因此它们应该是static。它本质上是一个实用程序类。

虽然 OOP 原则不适用,但当前的方式更清晰、简洁且更具可读性,因为您不必担心多态性和继承。这一切都缩小了范围,最终reduces the chances that you screw something up

现在,您可能会问自己“为什么我不能在 Java 中扩展数组的功能?”。一个很好的答案是这个introduces potential security holes,它可能会破坏系统代码。

【讨论】:

    【解决方案2】:

    对我来说,这是完美的解决方案。我有一个数组,还有一个类 Arrays,它对数组中的数据进行操作。例如,您可能想要保留一些随机数,并且您永远不会想要排序或任何其他实用程序方法,您将收到您不想要的行为。这就是为什么在代码设计中最好将数据与行为分开。

    您可以阅读有关single responsibility principle 的信息。

    【讨论】:

    • 只是想知道(从未使用过Java):您可能还有一个只需要打印的字符串"abc",但它带有一堆实例方法。你不是在本质上得到你不想要的行为吗?还是我缺少一些差异(因为我不懂语言)?
    【解决方案3】:

    良好的观察力。另请注意,并非每个数组都可以排序。只有实现Comparable 接口的基元和对象数组才能被排序。因此,不可能使用适用于所有数组的通用 sort() 方法。因此,对于每种支持的类型,我们都有几个重载的静态方法,它们实际上是可排序的。

    更新:

    @Holger 在下面的 cmets 中正确指出,其中一个重载的静态方法确实是 Arrays.sort(Object[]),但文档明确指出:

    数组中的所有元素都必须实现Comparable接口。

    因此它不适用于未实现 Comparable 或其子接口之一的对象。

    【讨论】:

    • 其实有原始类型的方法,有Arrays.sort(Object[])。当说没有通用的sort() 方法时,您想到的是哪种数组类型?请注意,当使用不可比较的数组类型但可比较的元素时,所提到的变体甚至可以完美地工作,例如Object[] array={ "foo", "bar" }; Arrays.sort(array);
    • docs for Arrays.sort(Object[]) 明确表示“数组中的所有元素都必须实现 Comparable 接口。”。这可能是从前泛型时代遗留下来的。此外,java.lang.String 确实实现了Comparable 接口。
    • 是的,正如我所说,元素是可比较的,但数组类型是Object[],它不是Comparable[]的子类型。但是,如上所述,此约束不是由签名强制执行的。您的回答给人的印象是,所提供的方法有点对不可比较的数组进行排序,而实际上您可以传入任何数组,并且只会在运行时注意到元素不可比较时(这并没有随着泛型而改变)。
    • Arrays.sort(Comparable[]) 不起作用,因为泛型和数组通常不能很好地结合使用:采用数组的方法通常必须接受 reifiable types,而泛型不是由于类型擦除造成的。
    • @Asaph:当然,在引入泛型时,为了向后兼容,签名必须保持不变。例如。请注意,Comparable 现在具有通用签名 Comparable<T> 而不是 Comparable<T extends Comparable<T>>(如 Enum<E extends Enum<E>>)。原因是,无论出于何种原因,compare 方法的原始预泛型签名是compare(Object),而不是compare(Comparable)。因此,必须以compare(T) 的擦除签名保持compare(Object) 的方式定义通用签名。棘手。
    【解决方案4】:

    数组不是存储状态的对象,超出了数组的实际值。换句话说,它只是一个“哑容器”。它不“知道”任何行为。

    实用程序类是一个只有public static 方法的类,这些方法是无状态函数。排序是无状态的,因为在调用该方法之间没有任何记忆。它“独立”运行,将其公式应用于传入的任何对象,只要该对象是“可排序的”。 Arrays 类的第二个实例的行为没有什么不同,所以只需要一个 static 实例。

    正如 Dariusz 指出的,有不同的排序方式。所以你可以有MyArrays.betterSort(array)Arrays.sort(array)

    如果您想让数组“知道”如何最好地对自己的成员进行排序,您必须拥有自己的数组类,其中extends 是一个数组。

    但是,如果您希望在同一个数组的不同时间进行不同的排序,该怎么办?可能是人为的例子,但现实世界中有很多类似的例子。

    现在你变得复杂了。也许T 类型的数组与S 类型的排序不同 ....

    使用静态实用程序和Comparator<T> 接口使事情变得简单。

    【讨论】:

      【解决方案5】:

      数组有点像二等泛型。当你创建一个数组时,它会为数组类型创建一个自定义类,但它的功能并不完整,因为他们在真正充实语言之前就决定了数组的工作方式。

      这与保持向后兼容性相结合,意味着数组被困在一个过时的接口上。​​

      这只是 API 的一个旧部分。

      【讨论】:

        【解决方案6】:

        首先,Arrays 是一个实用程序类,它正是这样做的:公开静态方法。它与任何arr[] 实例是分开的,并且与它没有OO 关系。有几个这样的类,比如Collections 或各种StringUtils

        数组是集合,它们用于存储数据。 Arrays.sort() 是一种对集合进行排序的算法。可能有许多其他算法以不同的方式对数据进行排序,所有这些算法都将以相同的方式使用:MyAlgorithm.doSthWithArray(array)。即使数组上有sort() 方法(它必须是SortableArray,因为并非所有对象都可以自动排序),所有其他算法都必须调用旧方法 em> 无论如何。除非引入了访问者模式……但这会让事情变得太复杂,因此没有意义。

        对于 java Collection,有 Collections.sort(),即使在 C++ 中也有 std::sort,其工作方式与 C 中的 qsort 类似。我在这里没有看到问题,我看到了一致性。

        【讨论】:

        • 请注意,在 Java 8 中有一个 List.sort 实例方法。
        • 同意,这不是语法糖,我们称之为便捷方法。这很不方便,因为它可能会引入运行时异常,通常编译时检查会找到错误。
        • 这是诊断 OP(可理解的)误解的正确答案:Arrays 不是数组的类型,因此它的静态方法在不改变语言的情况下不能成为数组的成员函数。这与Integer 等包装器类型不同(顺便说一句,这些包装器是混合体,因为它们 携带值的对象以及通过提供诸如static String toString(int i).)
        • @Dariusz:这不仅仅是一种方便的方法,因为与Collections 中的static 方法不同,实现现在可以覆盖sort 方法,这是为了提高效率而广泛使用的ArrayList ,而synchronizedList 返回的列表这样做是为了提供以前版本中不存在的线程安全性。关于类型安全,尝试在没有声明的可比较元素类型的情况下自然地对列表进行排序总是通过Collections.sort(list, null) 工作。如果你不喜欢它,你可以使用list.sort(Comparator.naturalOrder()),它检查就像Collections.sort(list)
        • @Dariusz:是的,但如前所述,您可以使用Collections.sort(list, null) 绕过该检查。因此,您可以选择使用已检查的无比较器 Collections.sort(list) 或未检查的 Collections.sort(list, null)。同样,在对列表调用排序时,您可以选择使用选中的list.sort(Comparator.naturalOrder()) 或未选中的list.sort(null)。不幸的是,不可能提供经过检查的无比较器list.sort()
        【解决方案7】:

        在 Java 中,无法扩展数组的功能。数组都继承自Object,但这几乎没有。恕我直言,这是 Java 的缺陷。

        相反,为了添加数组功能,静态实用程序方法被添加到 ArrayArrays 等类中。这些方法是static,因为它们不是实例方法。

        【讨论】:

        • 但是 java 数组有一个length 属性,它不是来自Object。那么为什么不能添加其他属性甚至方法呢?
        • @Asaph length 只读属性在 JLS 中定义,并且有自己的字节码指令。 arraylength 扩展它并不像给类添加方法那么简单。
        • 我同意这是 Java 设计的一个缺陷。至少,equals()、hashCode() 和 toString() 的实例版本应该已经为数组类正确实现了。但由于严格的向后兼容性政策,他们拒绝这样做。
        • @friendlydog 您可以通过更改Object.tostring() vanillajava.blogspot.com.au/2016/03/… 的实现来添加到toString()
        • 虽然我同意这个答案的第二部分,但我发现第一个是不正确的。 Oracle(通过 JCP)可以更新语言规范以向数组添加排序方法。然而,在 Java 中,数组比许多新语言中的数组更原始,所以我确实认为添加排序方法不是一个好主意,但如果他们有足够的动力,他们可以做到。在这种情况下,使用实用程序类更有意义。
        【解决方案8】:

        静态方法有时用于实用目的。 所以 Arrays 是用于通用数组操作的实用程序类。 同样,Collections 也是提供实用方法的 Util 类。

        【讨论】:

          猜你喜欢
          • 2012-08-03
          • 1970-01-01
          • 2011-10-11
          • 1970-01-01
          • 2019-04-14
          • 2013-05-18
          • 2011-03-12
          • 2015-08-10
          • 1970-01-01
          相关资源
          最近更新 更多