THE Java™ Programming Language, Fourth Edition

(Jeff_L) #1

This class relationship allows polymorphism for arrays. You can assign an array to a variable of type Object
and cast it back. An array of objects of type Y is usable wherever an array of objects of its supertype X is
required. This seems natural but can require a run time check that is sometimes unexpected. An array of X can
contain either Y or Z references, but an array of Y cannot contain references to X or Z objects. The following
code would generate an ArrayStoreException at run time on either of its final two lines, which violate
this rule:


Y[] yArray = new Y[3]; // a Y array
X[] xArray = yArray; // valid: Y is assignable to X
xArray[0] = new Y();
xArray[2] = new X(); // INVALID: can't store X in Y[]
xArray[1] = new Z(); // INVALID: can't store Z in Y[]


If xArray were a reference to a real X[] object, it would be valid to store both an X and a Z object into it.
But xArray actually refers to a Y[] object so it is not valid to store either an X reference or a Z reference in
it. Such assignments are checked at run time if needed to ensure that no improper reference is stored into an
array.


Like any other object, arrays are created and are subject to normal garbage collection mechanisms. They
inherit all the methods of Object and additionally implement the Cloneable interface (see page 101) and
the Serializable interface (see "Object Serialization" on page 549). Since arrays define no methods of
their own, but just inherit those of Object, the equals method is always based on identity, not
equivalence. The utility methods of the java.util.Arrays classsee "The Arrays Utility Class" on page
607allow you to compare arrays for equivalence, and to calculate a hash code based on the contents of the
array.


The major limitation on the "object-ness" of arrays is that they cannot be extended to add new methods. The
following construct is not valid:


class ScaleVector extends double[] { // INVALID
// ...
}


In a sense, arrays behave like final classes.


Exercise 7.3: Write a program that calculates Pascal's triangle to a depth of 12, storing each row of the triangle
in an array of the appropriate length and putting each of the row arrays into an array of 12 int arrays. Design
your solution so that the results are printed by a method that prints the array of arrays using the lengths of
each array, not a constant 12. Now change the code to use a constant other than 12 without modifying your
printing method.


7.5. The Meanings of Names


Identifiers give names to a range of things within our programstypes, variables, fields, methods, and so forth.
When you use a particular name in your program, the compiler has to determine what that name refers to, so
that it can decide if you are using the name correctly and so that it can generate the appropriate code. The
rules for determining the meaning of a name trade off convenience with complexity. At one extreme the
language could require that every name in a program be uniquethis makes things simple for the compiler but
makes life very inconvenient for the programmer. If names are interpreted based on the context in which they
are used, the programmer gets the convenience of reusing names (such as always using the name i for a for

Free download pdf