Arrays and the Class Hierarchy

At the end of Chapter 4, we mentioned that arrays have a place in the Java class hierarchy, but we didn’t give you any details. Now that we’ve discussed the object-oriented aspects of Java, we can give you the whole story.

Array classes live in a parallel Java class hierarchy under the Object class. If a class is a direct subclass of Object, then an array class for that base type also exists as a direct subclass of Object. Arrays of more derived classes are subclasses of the corresponding array classes. For example, consider the following class types:

class Animal { ... }  
class Bird extends Animal { ... }  
class Penguin extends Bird { ... }

Figure 6.8 illustrates the class hierarchy for arrays of these classes. Arrays of the same dimension are related to one another in the same manner as their base type classes. In our example, Bird is a subclass of Animal, which means that the Bird[] type is a subtype of Animal[]. In the same way a Bird object can be used in place of an Animal object, a Bird[] array can be assigned to a variable of type Animal[]:

Animal [][] animals;  
Bird [][] birds = new Bird [10][10];  
birds[0][0] = new Bird( );  
  
// make animals and birds reference the same array object  
animals = birds;    
Observe( animals[0][0] );               // processes Bird object
Arrays in the Java class hierarchy

Figure 6-8. Arrays in the Java class hierarchy

Because arrays are part of the class hierarchy, we can use instanceof to check the type of an array:

if ( birds instanceof Animal[][] )      // true

An array is a subtype of Object and can therefore be assigned to Object type variables:

Object something;  
something = animals;

Since Java knows the actual type of all objects, you can also cast back if appropriate:

animals = (Animal [][])something;

Under unusual circumstances, Java may not be able to check the types of objects you place into arrays at compile time. In those cases, it’s possible to receive an ArrayStoreException if you try to assign the wrong type of object to an array element. Consider the following:

class Dog { ... }  
class Poodle extends Dog { ... }  
class Chihuahua extends Dog { ... }  
  
Dog [] dogs;  
Poodle [] poodles = new Poodle [10];  
  
dogs = poodles;   
dogs[3] = new Chihuahua( );  // runtime error, ArrayStoreException

Both Poodle and Chihuahua are subclasses of Dog, so an array of Poodle objects can therefore be assigned to an array of Dog objects. The problem is that an object assignable to an element of an array of type Dog[] may not be assignable to an element of an array of type Poodle[]. A Chihuahua object, for instance, can be assigned to a Dog element because it’s a subtype of Dog, but not to a Poodle element.[22]



[22] In some sense, this could be considered a hole in the Java type system. It doesn’t occur elsewhere in Java—only with arrays. This is because array objects exhibit covariance in overriding their assignment and extraction methods. Covariance allows array subclasses to override methods with arguments or return values that are subtypes of the overridden methods, where the methods would normally be overloaded or prohibited. This allows array subclasses to operate on their base types with type safety, but also means that subclasses have different capabilities than their parents, leading to the problem shown earlier.

Get Learning Java now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.