This series of major discussions on adding generic types to Java programming is one of the articles that will look at one of the two limitations on using generics that have not been discussed, that is, to add support for new operations on bare type parameters (such as new T () in class c<t>).
As I mentioned last month, Tiger and JSR-14 implement generic types for the Java language by using type erasure. Using type erasure, generic types are used only for type checking, and then they are replaced with their upper bounds. This definition indicates that the elimination will conflict with an expression such as new T ().
If the bounds of T are assumed to be object, then the expression is eliminated as new object (), and no matter how the T is instantiated (String, List, URLClassLoader, and so on), the new operation will produce an instance of object. Obviously, this is not what we want.
To add support for an expression such as new T (), and adding support for other types-related operations we discussed last time, such as data type conversions and instanceof expressions, we must adopt some implementation strategy rather than type elimination (for example, for each generic instantiation, separate classes are used). However, for the new operation, other issues need to be addressed.
In particular, to enable this support to be added to the Java language, a number of basic language design issues must be taken into consideration.
A valid constructor call
First, to construct a valid new expression (such as New T ()) for a type parameter, you must make sure that the constructor we call is valid for each instantiation of T. But since we only know that T is a subtype of its declared bounds, we do not know what constructor a particular instantiation of T will have. To solve this problem, you can use one of the following three methods:
All instantiations of a type parameter are required to include a (zeroary) constructor with no parameters.
Throws an exception whenever the Run-time instantiation of a generic class does not include the required constructor.
Modify the syntax of the language to include more detailed type parameter bounds.
1th method: constructor with no parameters required
Only require that all instantiations of type parameters include constructors without parameters. The advantage of this solution is very simple. There is also a precedent for using this approach.
The existing Java technology (like JavaBean technology) to handle similar problems is to solve the problem by requiring a constructor with no parameters. However, one of the main drawbacks of this method is that for many classes, there is no reasonable constructor with no parameters.
For example, any class that represents a Non-empty container must use a parameter representing its elements in the constructor. Including constructors without parameters forces us to create the instance first and then the initialization that could have been done in the constructor call. But this practice can lead to problems (you may want to read this column, "The run-on initializer Bug pattern", published in April 2002 for more information; see Resources.) )
2nd method: Throws an exception when the required constructor is missing
Another way to handle this problem is to throw an exception whenever the Run-time instantiation of a generic class does not include the required constructor. Note that an exception must be thrown at run time. Because of the incremental compilation model of the Java language, we cannot statically determine the instantiation of all generic classes that will occur at run time. For example, suppose we have a set of generic classes as follows:
Listing 1. " New operation of the naked type parameter
class C<T> {
T makeT() {
return new T();
}
}
class D<S> {
C<S> makeC() {
return new C<S>();
}
}