If an expression produces a value, that value is of some particular data type. In some cases, it is possible to determine the exact type that is produced by an expression, based on the types of the literals, variables, and methods that an expression references. For those expressions that produce object references, it is typically only possible to determine the type of the referenced object when the expression is evaluated at runtime.
The types of many expressions are ambiguous because of the way Java data types are defined. There is no ambiguity for variables, array elements, and method return values of primitive types, however. These expressions always produce the exact types specified in their declarations.
There can be ambiguity when a variable, array element, or method return value is declared to have a class or interface reference type. The ambiguity exists because a class reference may actually refer to an object of the intended class or a subclass of that class. For example, consider a variable that is declared to contain a reference to a Number object:
double square(Number n){ return n.doubleValue()*n.doubleValue(); }
When the Java compiler sees the variable n used in an expression, it knows that the object that is referenced could be an Integer, Long, Float, or Double object because the java.lang package defines those subclasses of Number. It is also possible, however, that the variable refers to some other subclass of Number defined elsewhere. All that the compiler can be certain of is that at runtime n will refer to an object of a subclass of Number. The variable n cannot refer to a Number object because Number is an abstract class, so there are no Number objects.
The one exception to the ambiguity of class-type object references occurs when the class used to declare a variable, array element, or method return type is a final class. If a class is declared to be final, it cannot be subclassed, so there is no ambiguity.
Ambiguity also exists if the type of a reference is an interface type, since the reference can refer to an object of any class that implements the interface. The actual class is not usually known until runtime.
The fact that the type of value produced by an object reference expression cannot be determined until it is evaluated at runtime can affect the evaluation of other expressions in the following ways:
However, if the compiler cannot determine the actual class of the object, the actual method to be called is determined at runtime, when the class is known. The compiler generates code to handle a runtime selection process called dynamic method lookup. The process begins by searching the actual class for an appropriate method. If there is no such method, the superclass of the class is searched, followed by its superclass and on up the inheritance hierarchy, until an appropriate method is found. This process ensures that the appropriate method gets called, even if the actual class of the object is a subclass of the type used for the object reference.
Even if the compiler cannot determine the actual class of the object, there is one case in which it does not need to generate code to handle dynamic method lookup. When the compiler selects the appropriate method in the object, if it finds that the method is declared final, it can be sure that it is the method to be called.
void foo (InputStream a[]) { a[0] = new FileInputStream("/dev/null"); }
Any array with elements that contain references to objects of class InputStream or any of its subclasses can be passed to the method foo() shown above.
FileInputStream f[] = new FileInputStream[3]; foo(f);
Since FileInputStream is a subclass of InputStream, the call to foo() does not cause any type-related problems at runtime. However, the following call to foo() does cause problems:
DataInputStream f[] = new DataInputStream[3]; foo(f);
This call causes an ArrayStoreException to be thrown at runtime. Although DataInputStream is a subclass of InputStream, it is not a superclass of FileInputStream, so the assignment is not legal.
References Array Types; Assignment Operators; Casts; Class Types; Interface Types; Method Call Expression; The instanceof Operator; The throw Statement