Java应该将数组视为对象吗?

7
我经常认为,允许使用数组作为真正的对象并拥有自己的方法而不是依赖于像Arrays、Arrays和ArrayUtils这样的辅助类,是一个好主意。
例如:
ints.sort();                 // Arrays.sort(ints);
int[] onemore = ints.add(8); // int[] onemore = ArrayUtils.add(ints, 8);

我确定我不是第一个想到这个主意的人,但是我搜寻其他人之前的文章时遇到了麻烦。有没有人能帮我提供一些有关这个话题的参考资料?
这个主意被认为是好还是坏?为什么?
这个实施起来有多容易?
一些其他的例子可能包括(但不必纠结于它们,它们与问题本身无关):
int[] ints = {5,4,3,2,1};

// Arrays.sort (ints);
ints.sort();

// int pos = Arrays.asList(ints).indexOf (5);
// int pos = ArraysUtils.indexOf (ints, 5);
int pos = ints.indexOf (5);

// Arrays.reverse (ints);
ints.reverse();

Array<Integer> array = ints; // cast to super class.

// int length = Array.getLength (array);
int length = array.getLength();

// Object n = Array.get (array, 3);
Object n = array.get (3);

// Array.set (array, 3, 7);
array.set (3, 7);

Object obj = array;
// if (obj instanceof int[])
//     System.out.println(Array.toString((int[]) obj));
// else if (....)
System.out.println (obj);

不确定你是否在开玩笑,比尔 :-) 这是转贴某人的问题,如我所写,希望能教育他们。这就是为什么我通过维基(wiki)提交了它 - 我并不想博取声望。 - paxdiablo
我一直在关注其他的帖子,但是我忍不住想要开个小玩笑。现在认真想想,这确实是一个不错的问题。 - Bill the Lizard
@Peter Lawrey,如果这个问题还存在(可能不会,但我可以接受),请告诉我您想要接受哪个答案,毕竟这是您最初的问题。否则,我将在大约一周内接受得票最高的答案。 - paxdiablo
还有一个不一致的地方。虽然数组不是对象,但它们具有长度属性,这对于原始类型来说是反直觉的。 - Learning
10个回答

7
在Java中,数组不是类,这是有原因的——它们很好地映射到人们使用C风格语言时对数组如何工作的理解。将数组作为低级容器而不是对象也有性能上的原因。因此,有时使用原始数组比使用集合(例如ArrayList)具有性能优势。
如果你想使用对象,你应该使用集合。虽然可能有些笨重,但集合提供了您所需的良好方法访问类型。

1
我同意在1995年将数组视为原始类型是有道理的,但我认为现在应该采用更面向对象的方法。就像自动装箱/拆箱一样,这使得数组已经可用的函数更加紧密地附着在一起。 - Peter Lawrey
2
数组不是原始类型。阅读 Class.isPrimitive 的文档。 - Jon Skeet
Vector是一个遗留的类,不要使用它,应该使用ArrayList代替。 - Andreas Petersson
Vector不是一个过时的类,它是现在ArrayList的线程安全版本。 - Esko
@Esko Vector是一个遗留类。请参见https://dev59.com/WnM_5IYBdhLWcg3waieJ - Miha_x64

5

这些方法看起来非常像Ruby或Python的惯用语法。不幸的是,在Java中你无法这样做(希望你可以)。

首先,正如其他人指出的那样,集合类已经为你做了这些。其次,myarray.sort()并不好用,因为你可能会创建一些未定义排序的对象数组。假设我有:

 Foo[] foos; 

而Foo不是可比较的。在foos.sort()上会发生什么?我们绝对不希望它只适用于原始类型。

int[] ints; 
ints.sort(); //legal
Object[] objects;
objects.sort(); //illegal

您当然不能让编译器仅允许可比对象的语法。一旦您需要处理像

这样的内容,情况就变得更加复杂了。

 myarray.add(new Foo());

Java中的数组是不可变的,因此它有点毫无意义。

如果打印一个数组时不会出现那些无用的

([I'm an array(*&(*

垃圾,不过。

ArrayUtils.add(),addAll()和remove()返回一个新数组,其中包含添加的元素。 - Peter Lawrey
这些方法在你不想要 ArrayList 的开销时,非常有用于管理数组。 - Peter Lawrey

3
是的。但我认为Java根本不应该有任何原始类型。我认为Java中的原始类型破坏了语言的简洁性。Java中的所有内容都应该是对象。无论对象是在堆栈还是堆上分配,都应该是JVM的实现细节,而不是语言结构。但我认为我的观点可能比大多数人更激进。
在自动装箱之前,处理原始类型和对象非常麻烦。
如果数组是对象并且可以自动装箱(并具有泛型!),我们可以得到类似以下内容的东西:
Array<Integer> myArray = new Integer[];
myArray.add(8);
int x = myArray[0];
....

或者

Array<Class<? extends MyBaseObject>> myArray = {ExtendedObject.class}
ExtendedObject object = myArray[0].newInstance();
....

我认为不能透明地将基元类型视为对象是不幸的。编译器/JVM应该能够解决这个问题。 ArrayUtils有两种方法toObject()和toPrimitive()。它可以有另一种方法来改变数组的类型。 - Peter Lawrey
我建议Array<Integer>和Integer[]是写同一件事的两种方式。 ;) - Peter Lawrey
现在想要在Java中修复这些问题已经太晚了。看看那些从一开始就将其作为设计的语言。例如,在Scala中,一切都是对象。 - Esko Luontola

2
是的,我认为数组应该有一个超出语言本身规定的API定义。特别是,Foo[]不实现Iterable<Foo>让人很烦恼。是的,我知道包装它很容易,但必须要这样做很烦人。
.NET在这方面做得比较正确,有一个主要用于非泛型访问的Array类型,以及各种泛型接口,这些接口被认为是由实际的数组实现的。例如:
IList<int> intList = new int[10];

“.NET泛型处理原始类型比Java更好。”
“我看到的唯一缺点是,数组在.NET中可以是协变的,但普通的泛型不行,并且使用非零基数组和矩形数组会有些混乱。”
“顺便说一下,一些人在这个主题中将数组称为原始类型。虽然它们确实有特殊处理,但它们并没有被定义为原始类型。从语言规范,第4.2节中可以看出:”

2
在回答SHOULD之前,我会告诉你这个问题的现状。
在Java中,数组被视为对象--例如,您可以将它们存储在List中。但是,它们是特殊的对象,因为它们继承自Object,但不使用相同的语法进行实例化或访问(感谢Peter的更正)。它们受到JVM的积极优化,以解决明显的性能问题,因此存储数组的方式取决于实现。
因此,Sun的论点是,如果他们为数组提供了对象API,可能需要满足某些硬件、软件或其他规范要求。
数组的唯一真正接口是System.arrayCopy静态方法或Array.*静态方法,它们将最有效地复制到/从数组中。
针对SHOULD的回应;虽然已经得到解决,但标准比答案更好:使用ArrayList。

数组只实现了clone()方法,其他所有方法都是从Object继承而来的。并不是说它们没有被实现或者不可用,它们都有。 - Peter Štibraný

1

这不是一个主意。问题在于它已经在集合上实现了。如果你想要疯狂一点,尝试扩展ArrayList。


ArrayList的不足之处在于对基本类型的支持效率不高,[]运算符会创建两个对象(ArrayList和Object[]),而实际上你只需要一个。 - Peter Lawrey

1

我认为这很难在不破坏多个层面的兼容性的情况下实现。

  • JVM对待数组与其他对象有些不同。创建/初始化是不同的:首先没有调用构造函数。你想如何添加新方法?如果通过添加超类,你会调用它的构造函数吗?
  • 数组在运行时知道元素类型,而泛型则不知道。如果您想为所有数组添加新的超类(就像您在原始问题中建议的那样),您会使其成为通用的吗?还要记住,Java中的数组是协变的,而泛型不是。
  • 数组在Java语言规范中指定了一组固定的方法/字段(所有来自Objects的方法,在JLS中明确命名,长度字段)。添加新成员可能会破坏依赖于此的现有客户端。(即数组不是您的随机类)
  • 数组序列化也可能会受到影响
  • 我相信还有更多的实现细节会使这变得极其困难:-(
  • 编译为使用新方法的程序将无法在旧的JVM上工作。更糟糕的是,根据实现,为旧JVM编译的程序可能无法在新JVM上使用修改后的数组。

我希望在“Array类”中直接拥有方法,但我认为现在不可能实现它。


1

我个人喜欢一切都是对象的想法,例如Smalltalk中已经实现了这个想法。然而,使一切,包括方法/函数都成为对象的后果非常深远(请看Smalltalk以理解我的意思)。在应用“一切都是对象”的规则时保持一致会导致语言与C(或Java)完全不同。

Java被设计成易于理解C程序员,它在这方面做得很成功,但与Smalltalk相比,它实际上并不是非常面向对象。它作为C变体的遗产在许多地方都有所体现,例如数组和基本数据类型存在的事实。

因此,回答是否应该这样做的问题,我会说不应该,因为如果改变这一点,Java将不再是Java。正如其他人所指出的,还有其他类可以使用Java中的对象处理可能在C中使用数组完成的任务,但我个人认为要彻底摆脱原始数据类型和数组最好使用另一种语言。


0
对于那些错过了之前已关闭的帖子的人。还有其他一些例子包括:
int[] ints = {5,4,3,2,1};
ints.sort(); // instead of Arrays.sort(ints);
int pos = ints.indexOf(5); // instead of Arrays.asList(ints).indexOf(5); or ArraysUtils.indexOf(ints, 5);
ints.reverse(); // instead of Arrays.reverse(ints);
Array<Integer> array = ints; // cast to super class.
int length = array.getLength(); // instead of Array.getLength(array);
Object n = array.get(3); // instead of Array.get(array, 3);
array.set(3, 7); // instead of Array.set(array, 3, 7);
Object obj = array;
System.out.println(obj); // prints [5,4,7,2,1] instead of having to
// if (obj instanceof int[]) System.out.println(Array.toString((int[]) obj)); else if (....)

int[] ints2 = ints.copyOf(2);
int[] ints3 = ints.subArray(2,4);
ints.sort(myComparator);
List<Integer> list = ints.asList();
Set<Integer> set = ints.asSet();
long total = ints.sum();
double avg = int.average();
int max = ints.max();
int max2 = ints.max(myComparator);
http://commons.apache.org/lang/api/org/apache/commons/lang/ArrayUtils.html
int[] onemore = ints.add(8); // instead of ArrayUtils.add(ints, 8);
int[] moreInts = ints.addAll(ints2); // instead of ArraysUtils.addAll(ints, ints2);
int[] oneless = int.remove(3); // instead of ArrayUtils.remove(ints, 3);
Integer[] integers = int.toObject();
int[] intsAgain = integers.toPrimitive();

我会将这个作为附录复制到问题中,Peter。 - paxdiablo

0

我认为这并不难的一个原因是,你可以通过修改Object来间接地添加方法(我同意这是一种真正的黑客方式,但它证明了它可以工作)。 通过向Object添加方法,以下代码

int[] ints = {5, 4, 3, 2, 1};
System.out.println("ints= "+ints);
ints.sort();
System.out.println("after sort() ints= "+ints);
ints = ints.add(6);
System.out.println("after add(6) ints= "+ints);

打印

ints= [5, 4, 3, 2, 1]
after sort() ints= [1, 2, 3, 4, 5]
after add(6) ints= [1, 2, 3, 4, 5, 6]

这适用于Java 5和6,编译器和IDE都按照我的预期处理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接