Java Arrays Should Be First Class Objects

[Voting on JavaDesignFlaws page.]

OK, maybe I'm stupid. Or maybe I just don't understand Java.

But why do we need java.lang.System.arraycopy and why do we need the java.util.Arrays class, with a zillion static array handling methods?

Shouldn't stuff you need to do to an array be done with calls to methods in the "array class?"

My guess why this was not done: Adding these methods to all array classes would be bad design. You would rather expect a common abstract superclass java.lang.Array for all array types. But that would allow people to subclass it in other ways than the usual arrays. This might lead to a bunch of security and accessibility and typing problems. Nevertheless your point is well taken, these issues are not resolved perfectly. -- OlafKummer

No, you would make it final and thus not subclassable, ala java.lang.reflect.Array, which in the continuing tradition of java.lang.String is a piece of useless, unextensible crap. If Sun had written even moderately decent classes, this wouldn't be as much of an issue. By the way, it's a good design decision to make String and Array final because this allows the VM to make important optimizations. Smalltalk-80 does this for certain classes, like SmallInteger. -- SunirShah

I expected that there were individual array classes: int array, String array etc. Currently these classes inherit directly from java.lang.Object. If additional methods were to be added they would have to inherit from some other class java.lang.Array. In this scenario, the hypothetical class java.lang.Array could not be final. You would have to add further rules to described just which classes can inherit from that class, which would complicate things. I agree about the finalness of String. -- OlafKummer

Wouldn't it be possible to have an abstract java.lang.Array class that has package private constructors (thereby preventing subclassing outside of java.lang)? Then the VM could dynamically create the String array, int array, foo.bar.Baz array classes as subclasses of a real Array superclass and prevent rogue subclassing. -- NateRiffe?

At least that would make possible violations more difficult. I still think that there might be people who start writing their own subclasses of Array in such a case, but it become sufficiently clear that they get the blame for trouble. -- OlafKummer


>> My guess why this was not done: Adding these methods to all array classes would be bad design. You would rather expect a common abstract superclass java.lang.Array for all array types. But that would allow people to subclass it in other ways than the usual arrays. This might lead to a bunch of security and accessibility and typing problems. Nevertheless your point is well taken, these issues are not resolved perfectly. -- OlafKummer <<

Actually, this topic is even more, well, topical now that Java has generics. There is no real reason why java arrays couldn't be immutable-size Lists. Even better, this would encourage some of array's syntactic sugar (like array[index]) to be applied to Lists in general. Add in some compiler magic to keep them fast and you've got, well, a better language...


Why on earth do people get so worked up?

This is an analysis, not a love in. While overrhetorical, I'm confident in my assertion. Sun should definitely improve the string processing given that Java is moving to JSP pages and the ilk. -- SunirShah

True, the implementation of String is very shortsighted in some areas. Read jwz's rant on the subject: http://www.jwz.org/doc/java.html. Array has always been annoying, especially when you have to convert between List and Array by doing

MyObject[] myobjs = (MyObject[]) myList.toArray(new MyObject[0]);

This should not be necessary. -- WillSargent.


Primitive types are not objects. Operators are not messages. Arrays seem to be someplace in the middle -- half object, half primitive. In my view it IS a big, gaping, bleeding - even mortal - wound. But the problem is bigger than just arrays, ever try doing complex arithmetic in Java? How about matrix arithmetic? The problem is the premature decision to surface primitive types directly, ala CeePlusPlus, instead of wrapping them ala Smalltalk. -- TomStambaugh


An array is a java.lang.Object. From the JavaLanguageSpecification http://java.sun.com/docs/books/jls/second_edition/html/arrays.doc.html#27805

You're trolling, right?

An array of Foobar is an instance of class Foobar[.

How do I add, change, or even access methods of Foobar[?

Please consider the following code fragment:

  doublefield0 = 0l;
  doublefield1 = 1l;
  double[]array0 = new double[2];
  double[]array1 = new double[2];
  booleanisField0Equal;
  booleanisField1Equal;
  booleanareArraysEqual;

array0[0] = field0; array1[0] = field0; array0[1] = field1; array1[1] = field1;

isField0Equal = array0[0] == array1[0]; isField1Equal = array0[1] == array1[1]; areArraysEqual = array0.equals(array1);

What should be the values of the three booleans?

The two arrays array0 and array1 are of the same class, have the same length, and contain the same objects (isField0Equal and isField1Equal are each, correctly, true).

I know I'm probably too stupid and stuck in my ways, but I have the bizarre idea that two arrays that have the same class, same length, and identical contents should be equal (not "==", because the result of array0==array1 is correctly false - but "equal", because array0 and array1 have the same value).

I assert that the value of areArraysEqual, false, is wrong. How shall I correct it? I know of no class upon which I can add a hashCode() and equals() method. The only reason that areArraysEqual answers false is that the object references are not the same object pointer: array0 and array1 are different references. So where do I add the "equals" method for array0 and array1? Where do I find the class corresponding to the type "double[]"?

In my IDE (VisualAgeJava), I find no entries for "double[" or "Double[". In fact, I can't find any entries for ANY of the classes whose name is like "Foobar[".

Perhaps I'm just too stupid to know an obvious answer. If so, you may delete all my comments on this page.

I think that asserting that "An array is a java.lang.Object" misses the forest for the trees. I argue that whatever it is that an array IS, is is broken and that JavaArraysShouldBeFirstClassObjects.

-- TomStambaugh


In my opion arrays are primitives. They are "kind of" objects, but they are primitives. By primitives I mean some type that is dirrectly understood and manipulated by the lower level of abstraction in the system (in this case the JVM). The fact that they are not "exactly" objects and that XXX[ classes are not exactly classes is demonstrated by the following experiment:

I sort of hate to reopen this, but I can't find support for this. I can't find a section about "instanceof" in javadoc, and in the language spec, I find the following (http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#80289):

The type of a RelationalExpression? operand of the instanceof operator must be a reference type or the null type; otherwise, a compile-time error occurs. The ReferenceType? mentioned after the instanceof operator must denote a reference type; otherwise, a compile-time error occurs.

At run time, the result of the instanceof operator is true if the value of the RelationalExpression? is not null and the reference could be cast (�15.16) to the ReferenceType? without raising a ClassCastException. Otherwise the result is false.

I see no reference to the superclass hierarchy here. Further, in my VisualAgeJava environment, I *am* able to cast an instance of Integer[] into a Number[] at runtime without an exception. Hence, it appears to me that instanceof is meeting the language spec - even though it is VERY braindead. Is it fair to cite this as an example of "meeting the letter of the law" while grossly violating the "spirit of the law"? -- TomStambaugh

Actually, you are right. The java language specification does detail the array types as an exception in this regard (see 5.1.4) as opposed to any other reference type. -- CostinCozianu

Here's the little program I wrote a while ago:

public static void main(java.lang.String[] args)
{
java.lang.Integer[] x= {
};
System.out.println(" Class: " + x.getClass());
System.out.println(" isInterface: " + x.getClass().isInterface());
System.out.println(" SuperClass?: " + x.getClass().getSuperclass());
System.out.println(
" SuperSuperClass?: " + x.getClass().getSuperclass().getSuperclass());
System.out.println(
"x instanceof Cloneable : " + (x instanceof Cloneable));
System.out.println(
"x instanceof Serializable : " + (x instanceof java.io.Serializable));
System.out.println("x instanceof Object : " + (x instanceof Object));
System.out.println("x instanceof Number[] : " + (x instanceof Number[]));
System.out.println(
"Number[] is assignable from Integer[]: "
+ (Number[].class.isAssignableFrom(x.getClass())));
System.out.println(
"Number [] is interface : " + Number[].class.isInterface());
System.out.println("x instanceof object[] : " + (x instanceof Object[]));
System.out.println(
"Object[] is assignable from Integer[]: "
+ (Object[].class.isAssignableFrom(x.getClass())));
System.out.println(
"Object [] is interface : " + Object[].class.isInterface());

//Listing method java.lang.Class[] classes= x.getClass().getClasses(); System.out.println("Begin listing classes"); for (int i= 0; i < classes.length; i++) System.out.println("\t" + classes[i]); System.out.println("End listing classes");

java.lang.Class[] declared_classes= x.getClass().getDeclaredClasses(); System.out.println("Begin listing declared classes"); for (int i= 0; i < declared_classes.length; i++) System.out.println("\t" + declared_classes[i]); System.out.println("End listing declared classes");

//Listing method java.lang.reflect.Constructor[] constructors= x.getClass().getConstructors(); System.out.println("Begin listing constructors"); for (int i= 0; i < constructors.length; i++) System.out.println("\t" + constructors[i]); System.out.println("End listing constructors");

java.lang.reflect.Constructor[] declared_constructors= x.getClass().getDeclaredConstructors(); System.out.println("Begin listing declared constructors"); for (int i= 0; i < declared_constructors.length; i++) System.out.println("\t" + declared_constructors[i]); System.out.println("End listing declared constructors");

//Listing method java.lang.reflect.Method[] methods= x.getClass().getMethods(); System.out.println("Begin listing methods"); for (int i= 0; i < methods.length; i++) System.out.println("\t" + methods[i]); System.out.println("End listing methods");

java.lang.reflect.Method[] declared_methods= x.getClass().getDeclaredMethods(); System.out.println("Begin listing declared methods"); for (int i= 0; i < declared_methods.length; i++) System.out.println("\t" + declared_methods[i]); System.out.println("End listing declared methods");

java.lang.reflect.Field[] fields= x.getClass().getFields(); System.out.println("Begin listing fields"); for (int i= 0; i < fields.length; i++) System.out.println("\t" + fields[i]); System.out.println("End listing fields");

java.lang.reflect.Field[] declared_fields= x.getClass().getDeclaredFields(); System.out.println("Begin listing declared fields"); for (int i= 0; i < declared_fields.length; i++) System.out.println("\t" + declared_fields[i]); System.out.println("End listing declared fields");

System.out.println("qed ... Bye");

}

This clearly shows that XXX[ classes do break a fair amount of assumptions about how classes, interfaces, reflection and static typeing work for regular objects, therefore I consider that arrays are primitives. Some drawbacks are fairly obvious, but not so widespread as to be a real annoyance in Java (there's plenty of others annoyances to worry about, at least for me). The next question is : Is this thing justified ?. -- CostinCozianu

Nice analysis, Costin. How does Java's version of arrays affect programming? As much as I agree with the contributors above, I can't say I've ever had a problem using arrays. The existing form of arrays is very usefully low-level in that it maps naturally to native structures. Such a mapping is tricky in an abstract environment. If I need a 'first class object', I make one.


How does Java's version of arrays affect programming?

I was bit yesterday (it provoked my comments) because I'm writing unit tests, in JUnit, for a method that returns an array of doubles. I wanted to use the following simple code fragment:

  double[]expected;
  double[]result;

result = invokeTopSecretProprietaryMethodWhoseNameCannotBeRevealed();

assertEquals(expected, result);

From the JUnit distribution:
  /**
   * Asserts that two objects are equal. If they are not
   * an AssertionFailedError? is thrown.
   * @param expected the expected value of an object
   * @param actual the actual value of an object
   */
  static public void assertEquals(Object expected, Object actual) {
  assertEquals(null, expected, actual);
  }

Since "An array is a java.lang.Object.", as an earlier contributor has observed, I had assumed (in my late-night coding frenzy) that this would work. It doesn't.

The work-around isn't hard, but I do feel that it constitutes a JavaDesignFlaw, hence my contributions to this page. -- TomStambaugh


Its worse than that&#65533;.

I have noticed that Costin's program produces the following output:

 Class: class [Ljava.lang.Integer;

&#65533;.. omitted lines &#65533;...

Begin listing declared fields End listing declared fields qed ... Bye

The thing is we all know that arrays do have a field; the length field. What's going on!

The length field on an array is not a field, it's an operator. This is blatantly obvious if you read the Java Virtual Machine Specification. The fact that the array length operator looks syntactically like a field (in Java - other languages that compile to Java bytecode may do this differently) is very unfortunate. It would have been better done as something like lengthof arrayInstance, similar to the instanceof operator.

OK - I understand now, this was Costin's point. Not only does it not show the length field but the stamdard methods; eqals(Object), toString() etc. Even though they are not displayed they can be called, so Costin's "qed" means "this proves it is not a proper object"

-- ChrisBrooking?

Chris, when I run Costin's code, it does display the methods inherited from Object (hashCode(), wait(), getClass(), equals(), toString(), notify(), and notifyAll()). It does not, however, show the length field.

Just to throw a little more kerosene on the fire, Costin's example code also demonstrates the truth of NoRealJavaMetaModel -- the absense of the "length" field reeks of hackery. In addition to proving that an array is not a proper object, it also demonstrates that Java does not conform to its own meta model, as limited as that is -- a CodeStench if there ever was one. -- TomStambaugh

Aagh - it depends on whether I use the SDK (1.2.2) or the java runtime (1.1.8)

Running a cut-down version of Costin's code:

JRE

 C:\Program Files\Oracle\JDeveloper 3.2.3\myclasses>jre-cp .package11.ShowFields
 Begin listing fields
 End listing fields
 Begin listing methods
public final native java.lang.Class java.lang.Object.getClass()
public native int java.lang.Object.hashCode()
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
 End listing methods
 exercise toString(): [I@1ea8c9
 exercise equals(Object):  false

SDK
 C:\Program Files\Oracle\JDeveloper 3.2.3\myclasses>java  -cp .  package11.ShowFields
 Begin listing fields
 End listing fields
 Begin listing methods
 End listing methods
 exercise toString(): [I@f3b
 exercise equals(Object):  false

The cut-down code is as follows:

 package package11;

public class ShowFields? {

// Based on original program on http://c2.com/cgi/wiki?JavaArraysShouldBeFirstClassObjects

public static void main(String[]args) {

//Object x = new Object() ; // uncomment this line and comment following one to show that filed printing works Object x = new int[] {1, 2} ;

java.lang.reflect.Field[] fields= x.getClass().getFields(); System.out.println("Begin listing fields"); for (int i= 0; i < fields.length; i++) System.out.println("\t" + fields[i]); System.out.println("End listing fields");

//Listing method java.lang.reflect.Method[] methods= x.getClass().getMethods(); System.out.println("Begin listing methods"); for (int i= 0; i < methods.length; i++) System.out.println("\t" + methods[i]); System.out.println("End listing methods");

// now try calling the object methods System.out.println("exercise toString(): " + x.toString()); System.out.println("exercise equals(Object): "+ x.equals(new int []{1,2}) ); } }

This is truly horrible...


CategoryJava


EditText of this page (last edited February 3, 2010) or FindPage with title or text search