This material is covered in the book as well as in lecture. See section 2.4 of Program Development in Java, "Type Checking," and please note that the book refers to "compile-time type" as "apparent type" and "run-time type" as "actual type".
Here is an example to help distinguish between compile-time types and run-time types:
import java.util.Vector;
import java.util.List;
...
Vector vec = new Vector();
List list = vec;
Object obj = list;
...
In the above code, there is only one instance of an object; the
object created by the expression new Vector(). Thus, it has a
run-time type of Vector; expressions of the form "new X(...)" will always return an instance of X. There are three variables:
vec, list, and obj. They all refer to the
same object, but each has a different compile-time type ( Vector,
List,
and Object,
respectively).The compiler will allow calls like vec.lastElement(), list.clear(), and obj.toString(), because those are all methods associated with the compile-time types for each of those variables. The compiler will also statically ensure that the Vector class actually implements all of those methods, because the Vector class subclasses Object and implements List. The compiler will not allow calls like obj.clear() or list.lastElement(), because those methods are not declared by the compile-time types associated with those variables, and therefore are not guaranteed to be implemented by every object that the variables might point to.
Vector vec2 = obj;
are also not legal, because the compiler cannot determine that the
object referred to by the obj variable is a Vector. However,
implementors can reason about the code that they write, and ensure
that obj will always point to an instance of a
Vector. If the implementor is sure of this, then she can
write the code:
Vector vec2 = (Vector) obj;
This inserts code to check that the object referred to by obj
is indeed a Vector, and thus guarantees that if the assignment
happens, it is safe to use vec2 as a Vector. Why do we need casts? To allow objects to be passed around in a generic fashion while preserving type safety. A good example of this is the Vector class itself; it holds a sequence of objects, but it doesn't care about the types of the objects that it holds. Therefore, to allow code to make one vector containing Integers and another vector of Strings, the designers of Java established a type-hierarchy where every class extends the Object class. Thus the Vector class only requires users to pass it variables of type Object as arguments, and so one only needs to implement a single Vector class (as opposed to a VectorOfString class and a VectorOfInteger class). This genericity comes at a price though; the Vector class also only guarantees that it will return instances of Object when queried; it is up to the implementor to keep track of what types of objects are put into the vectors so that they can be casted successfully to the appropriate type upon retrieval.
There are a variety of reasons for this decision; one simple one (that may surprise you) is that it is legal in Java to write the following code:
class Transaction {
int amount; Date date;
public Transaction(int amount, Date date) {
this.amount = amount;
this.date = date;
}
...
// the "void" keyword here is a return type,
// making this a method declaration, not a constructor.
public void Transaction(int amount, Date date) {
...
}
}
(Of course, just because you can do something does not mean
that you should. The above code tends to reflect bad style,
and some compilers, such as jikes, will even issue a warning upon
encountering such code. )
Question: What is super(name) in slide 9 ?
Answer: This is the counterpart to using "this(...);"
to delegate initialization to another constructor in the same class.
The statement "super(...);" delegates initialization as well,
but instead of delegating it to a constructor in the same class, it
delegates initialization to a constructor in the superclass.
In this case, the OverdraftAccount(String, int) constructor first delegates initialization to the Account(String) constructor, and then runs its own initialization code.
See your Java text for more details.
Question: How come transactions Vector was initialized
and name wasn't?
Answer: Both are initialized. The code for initializing
transactions is located outside of the constructor, which
means that the creation of a new Vector and assignment to the
transactions field is implicitly done for each new instance
of an Account, before the constructor initialization code is
called. The code for initializing name is located within the
constructor, which means that it is called during the execution of the
constructor initialization code.
Note that the code given is functionally equivalent to:
class Account {
String name;
Vector transactions;
int balance;
Account(String name) {
this.transactions = new Vector();
this.balance = 0;
this.name = name;
}
...
}
It is merely a matter of stylistic preference to choose one over the
other; the style presented in lecture is convenient at times but can
also lead to subtle bugs if used without care.
Thus, you can predict which version of check will be called based on your knowledge of what will be the run-time type of the receiver object. In the example given in lecture, a call to post on an object whose run-time type is OverdraftAccount will cause the OverdraftAccount.check(Transaction) method to be called, while a call to post on an object whose run-time type is Account will cause the Account.check(Transaction) method to be called. Note that in both cases, it is the code for post in the Account class that is called!
You should read section 2.5 of the text, "Dispatching," for more information. If the material is still unclear after you've read the text a few times, talk to your TA.