In-depth understanding of Java generics

Source: Internet
Author: User
Tags comparable instance method object object throwable

1. Why why--introduces a generic mechanism

If we want to implement a string array and require it to dynamically change size, then we all think of aggregating a string object with ArrayList. However, after a while, we wanted to implement an array of date objects that could be changed in size, and of course we wanted to be able to reuse the ArrayList implementation of the string object that was written before.

Prior to Java 5, the implementation of ArrayList was roughly as follows:

 1  public  class   ArrayList { 2  public  Object Get (int   i) {...}  3  public  void   add (Object o) {...}  4   ...  5  private   object[] Elementdata;  6 } 

From the above code, we can see that the add function for adding elements to ArrayList receives an object type argument, and getting the specified element from ArrayList gets a method that also returns an objects of type An array of object objects elementdata the objects in this ArrayList, that is, whatever type of type you put into the ArrayList, inside it, is an object.

An inheritance-based generic implementation poses two problems: the first question is about the Get method, and each time we call the Get method we return an object, and each time it is forced to convert the type to the type we need, which can be cumbersome; the second question is about the Add method, If we add a file object to the ArrayList that aggregates the string object, the compiler will not generate any error prompts, and this is not what we want.

So, starting with Java 5, ArrayList can be used with a type parameter (type parameter), which is used to indicate the type of element in ArrayList. the introduction of type parameters resolves the two issues mentioned above, as shown in the following code:

New Arraylist<string>(); S.add ("abc"// no forced conversion S.add (123);  // compilation error, you can only add string objects to it ...

In the above code, after the compiler "learns" the type parameter string for ArrayList, it does the work of forcing type conversion and type checking for us.

2. Generic class

the so-called generic class (generic Class) is a class with one or more type parameters. For example:

 Public classPair<t, u> {    PrivateT first; PrivateU Second;  PublicPair (T First, U second) { This. First =First ;  This. Second =second; }     PublicT GetFirst () {returnFirst ; }     PublicU Getsecond () {returnsecond; }     Public voidSetfirst (T newvalue) { First=NewValue; }     Public voidSetsecond (U newvalue) {second=NewValue; }}             

As we can see in the above code, the type parameter of the generic pair is T, U, and is placed in the angle brackets after the class name. Here T is the first letter of type, representing the meaning of the types, commonly used are E (element), K (key), V (value) and so on. Of course, it is perfectly possible to use these letters to refer to type parameters.

When instantiating a generic class, we only need to change the type parameter to a specific type, such as instantiating a pair<t, u> class we can:

New Pair<string, integer> ();

3. Generic methods

The so-called generic method, which is a method with a type parameter, can be defined either in a generic class or in a normal class. For example:

 Public class Arrayalg {    publicstatic <T> T getmiddle (t[] a) {        return a[ A.LENGTH/2];}    }

The Getmiddle method in the code above is a generic method, with the format defined as the type variable placed after the modifier, preceded by the return type. As we can see, the above generic methods can be called for various types of arrays, and while the types of these arrays are known to be finite, they can also be implemented with overloading, but the coding efficiency is much lower. The sample code that calls the above generic method is as follows:

String[] strings = {"AA", "BB", "cc"= arrayalg.getmiddle (names);

4. Qualification of type variables

In some cases, a generic class or a generic method wants to impose some additional restrictions on its own type parameters. For example, we want to limit the type parameter to a subclass of a class, or to a class that implements an interface only. The associated syntax is as follows:

<t extends Boundingtype> (Boundingtype is a class or interface). The boundingtype can be more than 1, with a "&" connection.

5. In-depth understanding of generics implementation

In fact, from a virtual machine perspective, there is no "generics" concept. For example, the generic class pair we defined above, in the view of the virtual machine (that is, after compiling the bytecode), it looks like this:

 Public classPair {PrivateObject first; PrivateObject Second;  PublicPair (object First, object second) { This. First =First ;  This. Second =second; }     PublicObject GetFirst () {returnFirst ; }     PublicObject Getsecond () {returnsecond; }     Public voidSetfirst (Object newvalue) { First=NewValue; }     Public voidSetsecond (Object newvalue) {second=NewValue; }}

The above class is obtained through type erasure and is the original type (raw)of the pair generic class. Type erasure is to replace all type parameters with Boundingtype (if not qualified, replace with object).

We can simply verify that after compiling Pair.java, type "javap-c-S Pair" to get:

The line with "descriptor" is the signature of the corresponding method, such as from line fourth we can see that the two parameters of the pair construction method have been changed to object after the type erase.

Because the generic class pair changes to its raw type in the virtual machine, the GetFirst method returns an object, and from the compiler's point of view, this method returns the object of the type parameter that we specified when we instantiated the class. In fact, it was the compiler that helped us do the forced type conversion. This means that the compiler translates the call to the GetFirst method in the pair generic class into two virtual machine directives:

The first is a call to the raw type method GetFirst, which returns an object, and the second instruction casts the returned object object type to the type parameter type we specified.

Type erasure also occurs in generic methods, such as the following generic methods:

 Public Static extends comparable> T min (t[] a)

After compilation, the type erase becomes the following:

 Public Static Comparable min (comparable[] a)

The type erasure of the method brings some problems, consider the following code:

class extends Pair<date, date> {    publicvoid  Setsecond (Date second) {        if (Second.compareto (GetFirst ()) >= 0) {            super. Setsecond (second);        }    }    ...}

After the above code has been erased by type, it becomes:

class extends Pair {    publicvoid  Setsecond (Date second) {...}    ...}

The DateInterval class also has a Setsecond method inherited from the pair class (after the type erasure) as follows:

 Public void Setsecond (Object second)

Now we can see that this method has different method signatures (different parameters) than the DateInterval rewritten Setsecond method, so it is two different methods, however, these two methods should not be different methods (because it is override). Consider the following code:

New DateInterval (...); Pair<date, date> pair = interval;
Date adate = new Date (...); Pair.setsecond (adate);

The above code shows that the pair actually refers to the DateInterval object, so you should call DateInterval's Setsecond method, where the problem is that type erasure conflicts with polymorphism.

Let's comb why this problem occurs: pair is previously declared as type pair<date, Date> The class has only one "Setsecond (Object)" method in the virtual machine. So at runtime, when the virtual machine discovers that the pair actually refers to the DateInterval object, it calls DateInterval's "Setsecond (object)", whereas the DateInterval class has only "Setsecond (Date) Method

The way to solve this problem is for the compiler to generate a bridge method in DateInterval:

 Public void Setsecond (Object second) {    Setsecond ((Date) second);}

6. Precautions

(1) cannot instantiate type parameter with base type

In other words, the following statements are illegal:

pair<intintnew pair<intint> ();

But we can use the corresponding packaging type to replace.

(2) cannot throw or capture generic class instances

Generic class extension Throwable is illegal, so it is not possible to throw or catch a generic class instance. However, it is legal to use the type parameter in the exception declaration:

 Public Static extends void throws R =
try {
...
} catch (Throwable realcause) {
T.initcause (Realcause);
Throw T;
}
}

(3) An array of parameterized types is not legal

In Java, the object[] array can be the parent of any array (since any array can be transformed upward into an array of the parent class that specifies the element type when it is defined). Consider the following code :

string[] STRs = new STRING[10];
object[] Objs = STRs;
Obj[0] = new Date (...);

In the code above, we assign the array element to satisfy the parent class (object) type, but unlike the original type (Pair), it can pass at compile time and throw a Arraystoreexception exception at run time.

For these reasons, suppose Java allows us to declare and initialize a generic array with the following statement:

New Pair<string, string>[10];

Then after the virtual machine is type-erased, it actually pairs becomes the pair[] array, and we can transform it up to the object[] array. When we add pair<date, date> objects, we can check the compile-time and run-time, and our intention is to let this array store pair<string, string> objects, which will produce difficult to locate errors. Therefore, Java does not allow us to declare and initialize a generic array in the form of the above statement.

a generic array can be declared and initialized using the following statement:

New PAIR[10];

(4) Cannot instantiate type variable

Type variables cannot be used in the form of "new T (...)", "New t[...", "T.class". Java prohibits us from doing so simply because there is a type erase, so similar to "new T (...)" Such statements become "New Object (...)", which is usually not our intention. We can use the following statement instead of "New t[..." Invocation of:

New Object[n];

(5) A type variable cannot be used in a static context of a generic class

Note that we emphasize the generic class here. Because a static generic method can be defined in a normal class, such as the Getmiddle method in the Arrayalg class we mentioned above. Please consider the following code for the reasons why this is true:

 Public class People<t> {    publicstatic  T name;      Public Static T GetName () {        ...    }}

We know that at the same moment there may be more than one instance of People<t> class in memory. Suppose there is a People<string> object and a People<integer> object in memory now, and the static and static methods of the class are shared by all class instances. So the question is, is the name a string type or an integer type? For this reason, the use of type variables in the static context of a generic class is not allowed in Java.

7. Type wildcard characters

Before you introduce a type wildcard, you first introduce two points:

(1) Assuming that Student is a subclass of people, Pair<student, student> is not a subclass of Pair<people, people>, and there is no "is-a" relationship between them.

(2) Pair<t, t> and its original type Pair exist "is-a" relationship, Pair<t, t> in any case can be converted to pair type.

Now consider such a method:

 Public Static void printname (pair<people, people> p) {    = p.getfirst ();     // Suppose the People class defines the GetName instance method }

In the above method, we want to be able to pass in the parameters of Pair<student, student> and Pair<people, people> type at the same time, but there is no "is-a" relationship between them. In this case, Java provides us with a solution: using Pair<? Extends people> as the type of the formal parameter. That is to say, Pair<student, student> and Pair<people, people> can be regarded as pair< Subclasses of the extends people>.

Shaped like "<?" Extends boundingtype> "code is called the subtype qualification of a wildcard . corresponding to the wildcard character of the super-type qualification , the format is this: <? Super Boundingtype>.

Now let's consider this piece of code:

New Pair<student>(Student1, Student2); Pairextends people> wildchards =

The third row of the above code error, because Wildchards is a pair< Extends People> object, its Setfirst method and GetFirst method are this:

void extends People)
? Extends people GetFirst ()

For the Setfirst method, the compiler does not know what type the formal parameter is (only the subclass of the people), and we try to pass in a People object, the compiler cannot determine whether the people and formal parameter types are "is-a" relationships. So calling the Setfirst method will cause an error. The GetFirst method of calling Wildchards is legal, because we know it will return a subclass of people, and people's subclass "always be a people". (You can always convert a class object to a parent class object)

In the case of super-type qualification of wildcards, it is illegal to call the Getter method, and the calling setter method is legal.

In addition to subtype qualification and hyper-type qualification, there is also a wildcard character called an unqualified wildcard , which is such a:<?>. When are we going to use this thing? Considering this scenario, we call a Getpairs method that returns a set of Pair<t, t> objects. There are both pair<student, Student>, and Pair<teacher, teacher> objects. (There is no inheritance relationship between the student class and the teacher Class) Obviously, in this case, the subtype qualification and the super-type qualification are not available. At this point we can use such a statement to fix it:

pair<?>[] pairs = getpairs (...);

8. References

Java Core Technology (Volume 1) (watercress)

In-depth understanding of Java generics

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.