in Java go beyond the low-level, primitive built- ... Eiffel), arrays in Java conserve the traditional notation ... ed by the class Object, so they enjoy all the goodies.
Java To Go!
Arrays in Java: Second-class citizens? Miguel Katrib Mora
S
OME ENTHUSIASTIC AUTHORS1 have argued that arrays in Java are “first-class objects.” Certainly, we ought to be happy because arrays in Java go beyond the low-level, primitive builtin types: byte, short, int, long, float, double, boolean, and char. This affirmation is supported by the following Java array strengths. 1. For the benefit of mortal programmers, and unlike more “high-level arrays” (such as those in Smalltalk or Eiffel), arrays in Java conserve the traditional notation a[k] to refer to its components. 2. In contrast with some implementations of such aristocratic arrays, Java has its feet on the ground and preserves the basic efficiency of traditional arrays. Access and update operations for array components are directly implemented in the JVM, without the burden of method calls, and based on the essential of hardware for arrays: “the random access memory.” 3. At the same time, unlike low-level, C-like arrays that try to achieve maximum efficiency (sometimes crashing the application), in Java it is not possible to preclude the runtime control of “index out of bounds.” Such a safety and security rule is essential for Web programming. With an attempt to access a non-existent array component, execution will not be abruptly interrupted, but instead will be thrown an exception that can be caught by the program. 4. Unlike primitive objects, which are implemented by copy, array objects are implemented by reference so they can be shared while preserving their identity. 5. As true objects, array “birth” must be done explicitly, using the new operator, as in new T[10], or using the special syntax for array literal, e.g., {a, b, c}. The length of an array is defined during its construction, and is preserved throughout the array’s lifetime. The length can be queried (but not modified) from an array object through the length accessor. This allows, unlike Pascal and C, the definition of flexible and safe array-based methods using array parameters. 6. Arrays are included in the “patrician hierarchy” rooted by the class Object, so they enjoy all the goodies included in Object. But their “privileges” as members of this hierarchy are constrained, as we will see later. 7. Like all reference objects, arrays can be assigned to Object variables or passed to Object parameters, allowing them to participate in polymorphic situations.
50
Java Report | ™
MAY 2001
However, this polymorphism takes place in a mediated fashion because they cannot override any method. 8. Arrays must be seen as “containers” because they hold objects. Arrays are better than other Java containers whose components are of type Object because they have static-type safety. If a variable x is declared of type T[], then an attempt to put an object y with static type T’ in the container x through the assignment x[k] = y; will result in a compiling error if T’ is neither T nor a subtype of T. Unlike other containers, the “accessor method” for arrays, i.e., x[k], is recognized by the compiler as being of type T, without casting. Therefore, while we sadly wait for genericity in Java, Java arrays can be considered the only “real generic containers,” and array types the only “true case of parameterized types.” 9. An implicit and “parallel” type hierarchy exists for array types. If SubT is a subtype of T, then SubT[] is a subtype of T[]. Furthermore, if x is declared of static type T[], it can be attached at runtime to an object of type SubT[], but an attempt to assign an object y, of type T, to a component of the array x, will cause an exception to be thrown (see Listing 1). 10. Run-time type information (RTTI) and reflection apply for arrays. If the object attached to a variable x is of type T[], then the operation x instanceof T[] is valid and will evaluate true. The method x.getClass() will return a Class object corresponding to type T[]. In view of these strengths, we can say arrays in Java strike a balance between high abstraction, static and dynamic safety, and efficiency. Unfortunately, from a true OO perspective, these strengths are not enough. Unlike all other Java reference objects, arrays cannot redefine the Object’s methods and cannot define their own. As we will see, Java has adopted several “patched” solutions to mitigate this flaw.
Weaknesses of Java Arrays 1. The Java cloning policy does not apply to arrays. A programmer cannot specify that an array type implements the interface Cloneable. By default, Java compilers consider that array types automatically implement Cloneable, and a first-level, bit-copy clone operation is included for all array types. You may argue that this is a good thing, but what if you want a deeper clonation? Under the current Java approach, you can’t override
h t t p :/ / w w w . j a v a r e po r t. c o m
Java To Go! this clone method. 2. Even worse, the equals method does not comply with clone, but you cannot override equals. For example, if you have Date[] days = new Date[10], then Java allows you to clone it by means of twinDays = days.clone(). However, days.equals(twinDays) will evaluate to false. onelc(d w n .= at3. ysD iaysWhere to include general and useful methods for arrays? To solve this problem, JDK 1.2 introduces the class Arrays in the package java.utils. Among the methods in Arrays are the methods equals, fill, sort, and binarysearch. However, such methods are static and calls must be qualified with the class name passing the array as a parameter—e.g., java.utils.Arrays.sort(a). This is not too object-oriented. 4. It is possible in Java to define literal array objects, enumerating the values between curly braces, as in {x, y, z}. If x, y, and z have type T, the type of the object attached to a in the assignment T a = {x, y, z};, will be T. But if SuperT is a supertype of T, then the type of the object attached to b in the assignment SuperT[] b = {x, y, z};, will be SuperT[] and not T[]. Curiously, the same syntax and semantics do not apply when passing parameters. If a formal parameter of a method f is defined f(T[] x), then a caller is forced to write f(new T[] {x, y, z}). Seeing Java arrays’ strengths and weaknesses, we can conclude that Java arrays are not so “low level” and impious as Java primitive types or arrays in other languages, such as C. At the same time, Java arrays are not so “high level” as other Java reference objects or arrays in other OO languages, such as Eiffel2 (where an array is a true generic class).
1. All reference arrays implicitly inherit from the special abstract class Object[], and Object[] will inherit from Object. 2. The class Object[] will implement Cloneable. A public shallow bit-copy clone method should be included in Object[] (useful for all reference arrays). This method overrides the clone method inherited from Object. The equals method must be consistent with clone and also will be overridden in this class, i.e., if x is an array object, then x.equals(x.clone()) will evaluate to true, but x==x.clone() will evaluate to false. 3. The utility JDK 1.2 class Arrays could be deprecated because all its utility static methods should now be included in Object[] as instance methods for all reference arrays. 4. All arrays of primitive type (as int[]) should also be considered as special classes. The compiler should consider the methods clone and equals, and all the utility methods equals, fill, sort, and binarysearch (See number 3 in “Weaknesses of Java Arrays.”) defined now in Object[] for reference arrays (see previous point) as if they were built-in overridden in all the primitive array classes. 5. A class T[], letting T be either a reference type or a primitive type should be extendable like any other class. Inside the implementation of an instance method f of an extending class, the special variable this will denote the “parent array.” The notation this[k] should be allowed and it should refer to the kth component of the array object target to the call to f. The following example shows a class intArray and a method returning the sum of the array elements:
Improvements for “Better” Arrays The following suggested improvements preserve the strengths enumerated previously by improving arrays weaknesses and adding new capabilities.
LISTING 1 .. Java guarantees that an assign to an array component preserves the general dynamic type of the array. T[] x; ... x = new T’[10]; /*legal, T’[] is a subtype of T[] */ ... T’ a = new T’(); x[k]=a; /*legal T’ is the type of the components of x */ ... T y = new T(); x[i]=y; /* illegal, a runtime exception will be thrown because x has dynamic type T’[] and T does not conform to T’ */
52
Java Report | ™
MAY 2001
class intArray extends int[]{ public long sum(){ long temp=0; for (int k=0; k