First of all: good news! The final version of The Java Language Specification, Third Edition is now available online! The specification has been improved considerably since the latest draft. Gilad Bracha seems to be responsible for the bulk of the work, which is a tough job. I think that the result is pretty good, although I'm afraid that I will keep bothering him with comments and requests for clarification ;).
Now back to the issue of this post. First of all: I have no idea how well-known the issue in this post is. I didn't know it, but it might actually be quite well-known. I have some references to previous discussions on this issue at the end of the post.
First, I want to say something about what influences the return type of a method in Java. Before Java 1.5, the return type of a method was just the plain return type specified in the method declaration. In other words, the return type did not depend on anything.
Java 1.5 introduces parameterized types and generic methods. The return type of a method can now also include type variables. This makes the return type dependent on the values of the type variables that occur in it. The type variables can have two different scopes: the class of the method or just the method itself, which makes it a generic method. So, the actual return type of a method now also depends on the value of these type variables.
However, there is one method in the Java library that does not return what it declares to return and needs another dependency. Indeed, there is an additional factor that influences the return type of this method.
The method I'm talking about is
which returns the class of an object. In Java 1.5,
Class itself is parameterized with the type that it
represents. For example, the
Class<String>. The question
is: what should the type parameter of the
Object.getClass() be? Well, at the
declaration of the method we basically know nothing, and that is
indeed the declared return type: a wildcard (unknown type) with a
very general bounds: the type must extend
public final Class<? extends Object> getClass()
However, let's take a look at a piece of code where
getClass is invoked. Assuming that
getClass returns what it claims to return, we cannot
c to be of a more specific type, for example
Class<List>. We must declare it with a very general
value for the type parameter: a wildcard.
List<String> list = ...; Class<?> c = list.getClass();
This is unfortunate, since we actually know more about the type
Class. We know at the invocation
site that it is a
List, but of course we cannot
declare that in the return type of
getClass in this
way! So, we would like to let the return type of
getClass dependent on the static type of the
Object on which the method is invoked. In this way, the
variable c could be declared to be of type
The developers of the Java specification decided to make the return
type of this method a special case. That is, the Java Language
Specification defines that an invocation of the
getClass method must be treated in special way. In
other words, the return type of the method is different from the one
declared on the source code. The bounds of the
Object.getClass() is changed by the
specification to the static type of the expression on which the
getClass is invoked. This is a useful feature,
but it is a pity that this return type cannot be declared!
This post is getting way too long, but I would like to
relate this to the implicit
this argument of methods in
object-oriented languages. For ordinary method arguments, you can
declare types, which might include type variables. These type
variables can influence the return type of the method. This is more
or less what we want, but now we need this for our implicit
this argument. I'm not sure if a solution in this
direction is more attractive, but there is some link ... Are there
more methods whose return type we would like to dependent on the
static type of the object at the invocation site? If so, then this
should not be supported by the language itself. Unfortunately, I
cannot think of an example at the moment ;) .
There is even more to tell about this
since the type parameter of the
Class is not the static
type of the subject expression, but the erased variant of it. Maybe
I'll make that a future post ...
Some references to related discussions:
- Bug report in the Sun bug database about using the erased type as the parameter of the resulting Class: Object.getClass() should return erased class type
- Discussion in the Java Generics forum on the same issue: Are there bugs in the generics tutorial?
- Bug report for the Eclipse JDT subproject: Object.getClass() need to be treated special ?
- Another Generics FAQ: Is the capture of a bounded wildcard compatible to the bound?