This series of main discussions add generic types in Java programming, this article is one of them, and the two restrictions of using generics have not been discussed. (NEW T ()) in class C
As I mentioned last month, Tiger and JSR-14 implement generic types in Java languages by using "Type Erasure". Using Type Erasure, the generic type is only used for type check; then replace them with their upper bounds. This definitions can be seen: eliminating a conflict with expressions such as New T ().
If the boundary of T is Object, then this expression will be eliminated as new object (), and regardless of how to instantiate (String, List, UrlclassLoader, etc.), the New operation will generate a new Object instance. Obviously, this is not what we want.
To add support for expressions (such as New T ()), and add support for the other and types related to the last discussed (such as data type conversion and instanceof expressions), we must adopt some implementation strategy Instead of type elimination (such as per generic instantiation, use independent classes). But for the New operation, other problems are required.
In particular, in order to achieve this support to Java language, it is necessary to make a decision on many basic language design issues.
Effective constructor call
First, in order to construct a legal new expression (such as New T ()) for type parameters, it is necessary to ensure that the constructor we call is valid for each instantiation of T. However, because we only know that T is a subtype of its declaration, we don't know what constructor will have some examples of T. To solve this problem, you can use one of the three methods:
All instantiations of the required type parameters include unparalleled (ZEROARY) constructor. As long as the instantiation of the generic class is not included in the required constructor, it will throw an exception. Modify the syntax of the language to include more detailed type parameter boundaries.
The first method: There is a need for constructor that does not have parameters, only all instantiations of the type parameters include constructor without parameters. The advantage of this solution is very simple. A precedent is also available in this way.
Existing Java techniques for dealing with similar issues (like JavaBean technology) are to solve problems by requiring a constructor without parameters. However, one of the methods of this method is that for many classes, there is no reasonable constructor that does not have parameters.
For example, any class indicating a non-air container must use a parameter indicating its element in a constructor. Including constructor without parameters will force us to create an instance first, then perform initialization that can be completed in the constructor call. But this practice will result in problems (you may want to read this column "The Run-on Initializer Bug Pattern" published in April 2002 to get details; see Resources.)
The second method: Another way to throw an exception handling this problem is not included in the lack of the desired constructor. Please note: Exceptions must be thrown at runtime. Because of the incremental compilation model of the Java language, we cannot static determination of all generic classes that will occur at runtime. For example, suppose we have the following set of generic classes:
Listing 1. New operation of "naked" type parameters
Class C
Return new t ();
}
}
Class D {
C MAKEC () {
Return New C ();
}
}
Now, in class D , an example of class C is constructed. Then, in the main body of class C, the constructor of the non-parameter of the S is called. Does this constructor with parameters do? The answer is of course depends on the instantiation of S!
For example, if S is instantiated to String, then the answer is "existed". If it is instantiated as an Integer, then the answer is "not existed". However, when compiling class D and C, we don't know what other classes will construct what kind of D instantiation. Even if we have the entire program that can be used (we have never never have such a Java program), we must also conduct a considerable stream analysis to determine where the potential constructor may appear.
In addition, the incorrect type generated by this technology is difficult to diagnose and repair for programmers. For example, suppose the programmer is only familiar with the head of class D. He knows that the boundaries of the type of D is the default limit (Object). If you get such information, he does not reason to believe that the instantiation that satisfies the Declaring type boundaries (such as D
Also, the stack tracking of the reported errors may not include any way to call this bad D instance! Now let us assume that programmers have no right to access the source code of class C. He is what or how to correct the code will have no hair, unless he managed to contact class C's maintor and get clues.
Section 3: Modify the syntax to obtain more detailed boundary is another possibility to modify the language syntax to include more detailed type parameter boundaries. These boundaries can specify a set of available constructor that must appear in each instantiation of the parameter. Thus, in the extensive class definition, the only modread constructor is those that are declared in the limit.
Similarly, client classes that instantiate generic classes must use classes that meet the constraints of the constructor have declared. The parameters declaration will act as a contract between class and its clients so that we can quitly check if both the contract.
This method has many advantages over two other methods, which allow us to maintain the expression of the second method and the same static check in the first method. But it also needs to overcome the problem.
First, the type parameter declaration is easy to become lengthy. We may need a form of sweetness on some form of syntax, making these expansion parameters declare. In addition, if you add an extended parameter declaration in the version after Tiger, we must ensure that these expansion declarations will be compatible with existing compiled generic classes.
If you add support for generic types and types of operations to Java programming, what form is not clear. However, from which method will keep Java code as much as possible (and make it easy to correct when it is destroyed), the third option is undoubtedly the most suitable.
However, New expressions have another more serious problem.
Polymorphism
More serious problems is that there may be polymorphic recursions in the class definition. Polymorphism recursive occurs when the generic class is instantiated in its own body. For example, consider the following error example:
Listing 2. Self-references generic class
Class C
Public Object Nest (INT N) {
IF (n == 0) Return THIS;
ELSE RETURN NEW C
}
}
Assume that the client class creates a new C
Why is this a problem? Because if we support each instantiated independence class to support generic types related to type-related operations, then we can't know which classes we need to construct before running. However, if the class loader looks up existing class files for each class that it is loaded, what will it work?
Similarly, there are several possible solutions:
The instantiation of the extensive type of generic class can be set to the upper limit. Static prohibited polymorphism. A new instantiation class is constructed at the program.
Order 1: Set the maximum number of extensive classes that can be generated by the instantiation. Then, during the compilation, we can determine the limited boundary of a set of legal instantiations and only generate class files for all instantiation in this limit.
This method is similar to what to do in the C standard template library (this makes us reason to worry that it is not a good way). The problem is that the programmer will not predict that the program will be able to crash, like a report error for the wrong constructor. For example, suppose the limit of the instantiation is 42, and the previously mentioned Nest () method is called using the parameters provided by the user. So, as long as the user enters less than 42, everything is normal. When the user enters 43, this plan will fail. Now, imagine a poor code maintainer, what he faces is to re-combine code and try to figure out what specializes in the number of magic numbers 42.
The second type: Static prohibited polymorphic recursive why we do not send an order similar to "Static Prohibited Polymorphic Recursive"? (! If it is so simple.) Of course, many of the programmers including I will oppose this strategy, which inhibits many important design patterns.
For example, in a generic class list >? Returning from ways to this list is useful for building many commonly used data structures. It turns out that we cannot prevent polymorphic recursive, even if we want, Like the static detection of poor generic constructor, prohibiting polymorphic recursions will conflict with incremental class compilation. Our previous simple example (where polymorphic recursive occurs as a simple direct self-reference) makes this facts blur. However, since most classes compiled at different times are often used in any indirect level. Return once, it is because a generic class can instantiate another generic class with its own type parameters.
The following example relates to a polymorphic recursive between two classes:
Listing 3. Multistorus recursive
Class C
Public Object PotentialneSt (int N) {if (n == 0) Return this;
ELSE RETURN NEW D
}
}
Class D {
Public Object Nest (INT N) {
RETURN NEW C
}
}
It is clear that there is no polymorphism recursive in class C or D, but the expression of Nest (1000) will cause 1000 instantiation of class C.
Perhaps we can add new properties to class files to indicate all different generic types in the class, then analyze these instantiations when compiling other classes. However, we must also provide strange and intuitive error messages to programmers.
In the above code, where are we reporting an error? During the compilation process of class D or in the compilation process of client classes containing non-coherent expressions New D
Section 3: Another way to construct a new instantiation class in real time is to construct new instantiated classes attempts when the program is running. At first, this method seems to be completely incompatible with Java runtime. But in fact, all required to implement the policy is to use a modified class loader, which constructs a new instantiation class according to the "Template" class file.
The JVM specification has allowed programmers to use the modified class equipment; in fact, many popular Java applications (such as Ant, Junit and DRJAVA) use them. The disadvantage of this method is that the modified class equipment must be distributed together with its application to run on the older JVM. Because the type of equipment is often smaller, this overhead will not be large.
Let us study the work example of this method.
NEXTGEN Sample: Modified Class Implier Previous Method - With the modified class loader that instantiates the generic type of generic types to solve the polymorphic recursive problem - used by the NEXTGEN extension of the Java language. The modified class loader uses a template file that looks almost the same as the ordinary class file. Different is that this template file has some "holes" in the constant pool, filling these "holes for each instantiated class when loading ". Non-floaties are not affected.
In the JavaPLT programming language laboratory of Rice University, we recently released the prototype of the NextGen compiler, which is an extension of the GJ generic Java compiler, which is related to types of intersections (data type conversion, instanceof) Test and New Expression). In this prototype implementation, we used a modified class loader to support polymorphisms. This prototype is downloaded for free (see Resources).
Conclude
As shown in the above consideration, the mature runtime supports to the generic Java to solve many subtle design problems. If these problems are handled properly, the decrease in expressive and robustness will easily dispense the advantages of flipart. I hope that Java programming will continue to develop in the direction of maintaining these attributes and the direction of robustness.
Next time, we will end through the most powerful application generic type method - add the Mixin (class) to the language - to end the discussion of generic types. We will associate this MIXIN's performance with the powerful language characteristics previously discussed, discuss the advantages and disadvantages of adding Mixin through generic types.