Introduction to Java generic types

Source: Internet
Author: User

Note: This article is not original. ClickViewOriginal post

Java generic type, similar to the template in C ++, supports writing generic types starting from jdk1.5.
Example:
① PreviousCode
Import java. util. hashtable;
Class test {
Public static void main (string [] ARGs ){
Hashtable H = new hashtable ();
H. Put (New INTEGER (0), "value ");
String S = (string) H. Get (New INTEGER (0 ));
System. Out. println (s );
}
}
There is a forced data type conversion.

In Java (jdk1.5 ),Source codeThere is no forced conversion of data.

Class hashtable <key, value> {
...
Value put (key K, value v ){...}
Value get (key K ){...}
}
Import java. util. hashtable;
Class test {
Public static void main (string [] ARGs ){
Hashtable <integer, string> H = new hashtable <integer, string> ();
H. Put (New INTEGER (0), "value ");
String S = H. Get (New INTEGER (0); system. Out. println (s );}
}

② Generic Polymorphism

Class utilities {
<T extends Object> Public static list <t> make (t first ){
Return new list <t> (first );
}
}

Force make to construct a new instance
Utilities. Make (INTEGER (0 ))
 

③ Restricted Generic

Sometimes we want to restrict the possible type instantiation of generic classes. In the above example, the Type parameter of the hashtable class can be instantiated using any type parameter we want to use, but for some other classes, we may want to limit the possible type parameter set to the child type within the given type range.

For example, we may want to define a generic scrollpane class, which references a common pane with the scroll bar function. The runtime type of the contained pane is usually the child type of the pane class, but the static type is only pane.

Sometimes we want to use getter to retrieve the contained pane, but we want getter's return type to be as specific as possible. We may want to add the type parameter mypane to scrollpane. This type parameter can be instantiated using any subclass of pane. Then, you can use the clause in this form: extends bound to describe the declaration of mypane, so as to set the scope of mypane:

Listing 7. Using the extends clause to describe the mypane Declaration
Class scrollpane <mypane extends pane> {...}
 

④ Examples of other generic types
Class C <t> {
Static t member;

C (t) {member = T ;}

T getmember () {return member ;}

Public static void main (string [] ARGs ){
C <string> C = new C <string> ("test ");
System. Out. println (C. getmember (). tostring ());
New C <integer> (New INTEGER (1 ));
System. Out. println (C. getmember (). tostring ());
}
}

 

Import java. util. hashtable;
Interface registry {
Public void register (Object O );
}
Class C <t> implements registry {
Int counter = 0;
Hashtable <integer, T> values;

Public C (){
Values = new hashtable <integer, T> ();
}

Public void register (Object O ){
Values. Put (New INTEGER (Counter), (t) O );
Counter ++;
}
}

Java generics are easy to understand. Part 1
Overcome the limitations of generics in JSR-14 prototype Compilers

Level: elementary

Eric E. Allen, PhD student, JavaProgramming LanguageTeam, Rice University

June 09, 2003

Eric Allen, a Java developer and researcher, continues to discuss generic types in JSR-14 and tiger and focuses on adding new operations to generic types to support this branch.
This series mainly discusses how to add generic types in Java programming. This article is one of the two limitations on Using Generics that have not yet been discussed, add support for the new operation of the bare type parameter (for example, 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. Type erasure is used. generic types are only used for type checks. Then, they are replaced with their upper bound. We can see from this definition that the elimination will conflict with expressions such as new T.

If the limit of T is assumed to be object, the expression will be eliminated as new object (), regardless of how T is instantiated (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 other type-related operations (such as data type conversion and instanceof expressions) that we discussed last time, we must adopt some implementation policy rather than type elimination (for example, for each generic instantiation, use an independent class ). However, for the new operation, you need to solve other problems.

In particular, many basic language design issues must be decided to add such support to the Java language.

Valid constructor calls

First, to construct a valid new expression (such as new T () for type parameters, make sure that the constructor we call is valid for every instantiation of T. However, since we only know that T is the subtype of its declared boundary, we do not know what constructor will be available for a certain instantiation of T. To solve this problem, you can use one of the following three methods:

All instantiation of A type parameter is required to include a (zeroary) constructor without parameters.
If the runtime instantiation of a generic class does not include the required constructor, an exception is thrown.
Modify the syntax of a language to include more detailed type parameter boundaries.
1st Methods: constructors without Parameters

Only all instantiation of type parameters are required to include constructors without parameters. The advantage of this solution is that it is very simple. There is also a precedent for using this method.

The existing Java technology (such as the JavaBean technology) that handles similar problems is to require a constructor without parameters to solve the problem. However, a major drawback of this method is that there is no reasonable constructor without parameters for many classes.

For example, any class that represents a non-empty container must use a parameter that represents its element in the constructor. Including constructors without parameters will force us to first create an instance and then perform initialization that could have been completed in the constructor call. However, this practice may lead to problems (You may want to read this column published on April 9, April 2002 ).Article"The run-on initializer bug pattern" for details. See references .)

2nd Methods: when the required constructor is missing, an exception is thrown.

Another way to solve this problem is to throw an exception as long as the runtime instantiation of the generic class does not include the required constructor. Note: An exception must be thrown at runtime. Because of the incremental compilation model of the Java language, we cannot statically determine the instantiation of all generic classes that will occur at runtime. For example, suppose we have the following generic classes:

Listing 1. The new operation of the "Bare" type parameter

Class C <t> {
T maket (){
Return New T ();
}
}

Class D <S> {
C <S> makec (){
Return new C <S> ();
}
}
 

Now, in Class D <S>, the instance of class C <S> is constructed. In Class C, the constructor s without parameters will be called. Does this kind of constructor without parameters exist? The answer certainly depends on the instantiation of S!

For example, if S is instantiated as a string, the answer is "exist ". If it is instantiated as an integer, the answer is "nonexistent ". However, when compiling classes D and C, we do not know what d <S> instantiation will be constructed by other classes. Even if we have an entireProgram(We almost never have such a Java program), we still have to perform a very costly stream analysis to determine where potential constructor problems may occur.

In addition, the types of errors produced by this technology are difficult for programmers to diagnose and fix. For example, assume that the programmer is only familiar with the Class D header. He knows that the boundary of type parameters of D is the default boundary (object ). If such information is obtained, there is no reason to believe that the instantiation of d that satisfies the declared type limit (such as D <integer>) will cause an error. In fact, it won't cause errors for a long period of time until someone finally calls the method makec and (eventually) instantiate the call method maket for C. Then, we will get a report error, but this will take a long time after the actual problem occurs-Class D Bad instantiation.

Also, stack tracing for reported errors may not even include any method calls to this bad d instance! Now, let's assume that programmers do not have the right to compile the source code of class C. He has no clue about what the problem is or how to correct the Code unless he tries to contact the maintainer of class C and obtain clues.

3rd Methods: Modify the syntax to obtain more detailed limits

Another possibility is to modify the language syntax to include more detailed type parameter boundaries. These boundaries can specify a group of available constructors that must appear in each instantiation of the parameter. Therefore, within the generic class definition, the only callable constructor is the constructor declared in the boundary.

Similarly, the client class that instantiates a generic class must use a class that meets the constraints declared on the existence of the constructor. The parameter declaration acts as a contract between the class and its client, so that we can statically check whether the two comply with the contract.

Compared with the other two methods, this method has many advantages. It allows us to maintain the expressiveness of the second method and the same degree of static check as in the first method. But it also has problems to overcome.

First, the type parameter declaration can easily become lengthy. We may need some form of syntactic sweetness to make the parameter declarations of these extensions pass through. In addition, if you add the extended parameter declaration in Versions later than Tiger, we must ensure that these extended declarations are compatible with existing compiled generic classes.

If you add the support for generic type-related operations to Java programming, it is unclear about the form it uses. However, from the perspective of which method will keep Java code as robust as possible (and make it as easy as possible to correct when it is damaged), the third option is undoubtedly the most suitable.

However, the new expression has another more serious problem. 
 

 

Polymorphism Recursion

The more serious problem is that there may be multi-state recursion in the class definition. Polymorphism recursion occurs when a generic class instantiates itself in its own subject. For example, consider the following error example:

Listing 2. Self-referenced generic classes

Class C <t> {
Public object nest (int n ){
If (n = 0) return this;
Else return New C <t> (). Nest (n-1 );
}
}
 

Assume that the client class creates a new C <Object> instance and calls (for example) nest (1000 ). Then, during the execution of the nest () method, a new instantiated C <Object> is constructed and nest (999) is called for it ). Then, the constructor instantiates C <Object>, and so on until the constructor of 1000 independent class C is constructed. Of course, I choose the number 1000 at will. Normally, we cannot know which integers will be passed to the method nest at runtime. In fact, they can be passed in as user input.

Why is this a problem? If we construct an independent class for each Instantiation to support type-related operations of the generic type, we cannot know which classes we need to construct before the program runs. However, if the class loader finds existing class files for each class it loads, how does it work?

Likewise, there are several possible solutions:

Set the upper limit on the number of instantiated generic classes that can be generated by the program.
Static prohibit polymorphism recursion.
Construct a new instantiated class as needed when the program is running.
1st: Set the upper limit on the number of instances

We set an upper limit on the number of Instantiation classes that can be generated by the program. Then, during compilation, we can determine the finite boundary for the instantiation of a combination method, and only generate class files for all instantiation in the boundary.

This method is similar to what is done in the C ++ standard template library (which makes us have reason to worry that it is not a good method ). The problem with this method is that, like reporting errors for wrong constructors, programmers will not be able to predict that a certain running of their programs will crash. For example, assume that the limit of the number of instantiations is 42 and the user-provided parameters are used to call the previously mentioned nest () method. Then, everything works as long as the user input is smaller than 42. When the user inputs 43, the design of this plan will fail in weeks. Now, imagine that the poor code maintainer is facing the task of re-composing the code and trying to figure out what is special about magic 42.

2nd types: static prohibit polymorphism Recursion

Why don't we issue a command like "static prohibit polymorphism recursion" to the compiler? (Alas! It would be that simple .) Of course, many programmers, including me, will oppose this strategy, which limits the use of many important design patterns.

For example, in the generic list <t>, do you really want to prevent the construction of list <t>? Returning this list from the method is useful for building many frequently-used data structures. It turns out that we cannot prevent polymorphism recursion, even if we want. Just like static detection of poor generic constructor calls, disabling polymorphism recursion conflicts with incremental class compilation. Our previous simple example (where polymorphism recursion occurs as a simple and direct self-reference) will blur this fact. However, self-reference usually uses any indirect level for most classes compiled at different times. Again, it is because a generic class can be instantiated with its own type parameters.

The following example involves the polymorphism recursion between two classes:

Listing 3. Cross-recursive multi-state Recursion

Class C <t> {
Public object potentialnest (int n ){
If (n = 0) return this;
Else return new D <t> (). Nest (n-1 );
}
}

Class D <S> {
Public object nest (int n ){
Return new C <S> (). Nest (N );
}
}
 

Obviously, there is no polymorphism recursion in Class C or D, but expressions like new D <C <Object> (). Nest (1000) will cause 1000 instantiation of class C.

Perhaps, we can add new attributes to the class file to indicate that all different generic types in the class are instantiated, and then analyze the instantiation when compiling other classes for recursion. However, we still have to provide programmers with strange and inintuitive error messages.

In the above Code, where do we report errors? During the compilation of class D, is it still in the compilation process of the client class that contains the irrelevant expression new D <C <Object> (). Nest (1000? No matter which one, unless the programmer has the right to compile the source code of class C, he cannot predict when a compilation error will occur.

3rd types: Construct new instantiation classes in real time

Another method is to construct a new instantiated class as needed when the program is running. At first, this method seems completely incompatible with Java runtime. But in fact, all we need to implement this policy is to use a modified class loader, which constructs a new instantiated class according to the "template" class file.

JVM specifications allow programmers to use modified class loaders. In fact, many popular Java applications (such as ant, JUnit, and drjava) use them. The disadvantage of this method is that the modified class loader must be distributed with its application to run on an older JVM. Because the class loader is usually relatively small, this overhead is not large.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.