[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:
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�.
I have noticed that Costin's program produces the following output:
Class: class [Ljava.lang.Integer; �.. omitted lines �... Begin listing declared fields End listing declared fields qed ... ByeThe thing is we all know that arrays do have a field; the length field. What's going on!
-- ChrisBrooking?
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): falseSDK
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): falseThe 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...