![]() Princeton University |
Computer Science 441 |
A variable declared to be final must clearly be a constant (be sure to initialize it in the declaration or you will never be able to set it). A final method is one that may not be overridden. A final class is one that may not be extended.
In each case, the declaration of an entity to being final provides a guarantee to both the programmer and the compiler that the entity will never be changed again. This may result in a gain of efficiency for the compiler and better understanding for the programmer, but if mis-used can result in a loss of flexibility (the usual binding time trade-offs!).
A constant is almost always declared as public static final ... as it is generally visible, there only needs to be one copy for the entire class (there need not be a separate copy in each object) and it may not be changed.
If class C implements interface IC then objects of type C can be used in any context expecting an object of type IC.
While we haven't shown examples yet, one interface may also extend another. This also results in subtyping.
Subtyping is by declaration (name) in Java. Thus one type may be used as a subtype of another only if there is a chain of extends and implements going from the first to the second. For example if we have
SC extends C C implements IC IC extends IBCthen an object from class SC can be used in any context expecting an object of interface IBC.
As above, at run-time the contents of a variable of type (class or interface) C may actually be an object created by an extension. Thus one typically cannot determine statically whether the code from C (presuming C is a class) or one of its extensions will be executed. If C is an interface, then one has even less information statically about what code may be executed. This is generally considered a GOOD thing in object-oriented programming!
e = emp;results in e being a reference to the same value as originally given by emp.
It is worth noting here that constants holding objects aren't necessarily very constant. If e is a constant holding an HourlyEmployee then you may not assign to it, but you may send it messages like setHourlyPay(6.25) which will result in a change to the instance variables of the object. Thus constants do not change their identity, but object constants may change state!
Java does not have explicit pointers, as these are unnecessary. New objects are created by preceding the constructor of a class with the keyword new. Like ML, objects that are no longer accessible are swept up by the garbage collector and recycled.
You can create objects of these types using the fairly obvious constructors: new Integer(3), new Character('z'), etc. You can extract the values with fairly obviously named methods: i.intValue(), abc.charValue(), etc. Objects of these types are immutable. Thus you cannot change the value held inside an object of the wrapper classes. Of course if you have a variable of type Integer, you can create a new value of the type and assign it to replace the old value. Make sure you understand the distinction I am making here!
For those not used to it, this can seem very restrictive, especially since one cannot cheat like in C and pass an explicit pointer. However, one soon adjusts to the style and I suspect you will grow comfortable with it quite quickly.
public class DbleNode{ protected int value; protected DbleNode next, previous; public DbleNode(int value, DbleNode next){ this.value = value; this.next = next; }Let's take a quick time-out before writing the rest of the definition of the class. Notice that within the body of the constructor, DbleNode, the parameters value and next block the visibility of the instance variables value and next. Luckily we can refer to the instance variables by prefixing them with this. The net result is that the two assignment statements assign the parameter values to the corresponding instance variables.
This is a common idiom in Java that allows you to be lazy about thinking up new names for parameters that are different from the instance variables, but close enough to be suggestive of their intended meanings. Let's continue with the definition of the class:
protected void setPrevOnly(DbleNode pred){ previous = pred; // could have written this.previous, but not necessary. } public void setNext(DbleNode newNext){ next = newNext; if (newNext != null) newNext.setPrevOnly(self); } }The last method is interesting in that self is sent as a parameter in a message to newNext. While Java would not prevent me from assigning to newNext's instance variable (a method can access all date - no matter what the protection - of other objects of the same class!), it is considered very bad style to access the instance variables of other objects. Hence my use of the setPrevOnly method.
Thus of the basic data structures or building blocks for data structures, only arrays must be dealt with. Arrays in Java are objects, and hence must be created. The declaration for an array of employees would be written:
protected Employee[] emp;Technically, you can also write the declaration as in C++:
protected Employee emp[];but I prefer the former as it makes it clear what the type is and makes a bit more sense when the declaration also initializes the array:
protected Employee[] emp = new Employee[75];Notice that the declaration of the array does not include the size of the array, only the type of elements that it holds. Thus at various points in the program emp could hold arrays of different sizes. It is only the call to the constructor that determines the size of the array to be created. In particular, then, Java avoids Pascal's problems with passing arrays as parameters since the size of the array is not part of the type.
The programmer does not need to declare a constructor for arrays (in fact it's hard to imagine where one would put such a constructor). Thus, once the Employee class exists it is easy to construct an array of employees. However, one must be sure not to confuse creating an array with creating its elements.
Before the call of the constructor above, the value of emp was
null. After the execution of the constructor,
Multi-dimensional arrays are obtained by creating arrays of arrays. Java
does not provide the same nice abbreviations as Pascal to make you think
of them as multi-dimensional arrays. However, it does provide a bit more
flexibility:
If you read the previous comments about parameter passing in Java, you may
be wondering how you can change the values in an array if the array is
passed as a parameter. Luckily there is no problem. You still can't assign
to an array parameter as a whole (well, actually you can but it won't have
any impact on the original array ...), but you can assign to individual
elements of the array. Thus if emp is a formal parameter of array
type, then emp[3] = newEmployee changes the entry in slot 3 of
the actual parameter, at least if it has a third slot.
By the way, Java, unlike C++, automatically checks array boundaries when
accessing array entries. Arrays do keep track of their length, and you can
get access to the length of an array by writing emp.length. Be sure
not to include parentheses after length as it is an instance
variable rather than a method (bad Java designers, bad!).
Employee[][] squareEmps = new Employee[75][75];
// this builds a 75 by 75 array.
Employee[][] triangleEmps = new Employee[75][];
// this builds an array with 75 rows, each of which is currently empty.
for (index = 0; index < 75; index++)
triangleEmps[index] = new Employee[index+1];
The for loop assigns row index the size of index+1. Thus the 0th row
has size 1 up to the 74th row having size 75. Of course you still
need to initialize the individual elements of the array. You would get access
to an individual element by writing triangleEmps[3][2], for example.
CS441 |
CS Department | Princeton University