Design of Java generics

Source: Internet
Author: User
Tags comparable pear

design of Java generics
Introduction
generics areA very important point of knowledge in Java, generics are widely used in the Java Collection Class framework. In this article we will look at the Java generics design from scratch, which will involve wildcard processing and the distressing type erasure.

Generic Foundation
generic class
we begin by defining a simpleBox class:
Publicclass Box {
Private String object;
Publicvoid Set (stringobject) {This.object = object;}
Public String get () {return object;}
}
This is the most common practice, and one disadvantage of doing this isBox now can only be loaded into string type elements, in the future if we need to load other types of elements such as Integer, but also have to rewrite a box, the code is not reused, using generics can solve the problem well.
Publicclass box<t> {
T stands for "Type"
Private T T;
Publicvoid set (T t) {this.t = t;}
Public T get () {return t;}
}
this way, ourThe Box class can be reused, and we can replace T with any of the types we want:
box<integer> Integerbox = newbox<integer> ();
box<double> Doublebox = newbox<double> ();
box<string> Stringbox = newbox<string> ();
Generic Methods
after reading the generic class, let's look at the generic method. Declaring a generic method is simple, as long as you precede the return type with a similar<k, v> the form of the line:
Publicclass Util {
Publicstatic <k, v> boolean compare (Pair<k, v> p1, pair<k,v> p2) {
Returnp1.getkey (). Equals (P2.getkey ()) &&
P1.getvalue (). Equals (P2.getvalue ());
}
}
Publicclass Pair<k, v> {
Private K key;
private V value;
Public Pair (K key, V value) {
This.key = key;
This.value = value;
}
Publicvoid Setkey (K key) {This.key = key;}
Publicvoid SetValue (V value) {this.value = value;}
Public K GetKey () {return key;}
Public V GetValue () {return value;}
}
we can call the generic method as follows:
Pair<integer, string> p1 = new pair<> (1, "Apple");
Pair<integer, string> p2 = new Pair<> (2, "pear");
Boolean same = Util.<integer, String>compare (P1, p2);
or injava1.7/1.8 uses the type inference to automatically derive the appropriate type parameters for Java:
Pair<integer, string> p1 = new pair<> (1, "Apple");
Pair<integer, string> p2 = new Pair<> (2, "pear");
Boolean same = Util.compare (P1, p2);
Boundary character
now we're going to implement a feature that looks at the number of a generic array that is larger than a particular element, and we can do this:
publicstatic <T> int Countgreaterthan (t[] anarray, T elem) {
int count = 0;
for (T E:anarray)
if (e >elem)//Compiler Error
++count;
return count;
}
but it's obviously wrong, because in addition toShort,int, double, long, float, Byte, char and other primitive types, other classes are not necessarily able to use the operator >, so the compiler error, how to solve this problem? The answer is to use a bounding character.
Publicinterface comparable<t> {
Publicint compareTo (T o);
}
make a declaration similar to the following to tell the compiler type parameterT stands for classes that implement the comparable interface, which tells the compiler that they all implement the CompareTo method at least.
Publicstatic <t extendscomparable<t>> Intcountgreaterthan (t[] anarray, T elem) {
int count = 0;
for (T E:anarray)
if (E.compareto (Elem) > 0)
++count;
return count;
}
wildcard characters
before we know about wildcards, we first have to clarify a concept or borrow the one we've defined aboveBox class, let's say we add a method like this:
Publicvoid Boxtest (box<number> N) {/* ... */}
so nowWhat types of parameters are box<number>n allowed to accept? Can we pass in box<integer> or box<double>? The answer is no, although the Integer and Double are subclasses of number, there is no relationship between box<integer> or box<double> and box<number> in generics. This is very important, and then we'll take a complete example to deepen our understanding.
let's start by defining a few simple classes, which we'll use here:
Class Fruit {}
Class Apple extends Fruit {}
Class Orange extends Fruit {}

in the following example, we have created a generic classReader, and then in F1 () when we try Fruit f= fruitreader.readexact (apples); The compiler will make an error because there is no relationship between list<fruit> and list<apple>.
Publicclass genericreading {
Static List<apple>apples = Arrays.aslist (New Apple ());
Static List<fruit>fruit = Arrays.aslist (New Fruit ());
Staticclass reader<t> {
Treadexact (list<t> List) {
Return List.get (0);
}
}
Staticvoid F1 () {
reader<fruit> Fruitreader = newreader<fruit> ();
Errors:list<fruit> cannot is applied to list<apple>.
Fruit f =fruitreader.readexact (apples);
}
Publicstaticvoid Main (string[] args) {
F1 ();
}
}
But according to our usual habit of thinking,There's definitely a connection between Apple and fruit, but the compiler doesn't recognize it, so how do you solve this problem in generic code? We can solve this problem by using wildcard characters:
Staticclass covariantreader<t> {
T readcovariant (list<? extends T>list) {
Return List.get (0);
}
}
Staticvoid F2 () {
covariantreader<fruit> Fruitreader = newcovariantreader<fruit> ();
Fruit f = fruitreader.readcovariant (Fruit);
Fruit a = fruitreader.readcovariant (apples);
}
Publicstaticvoid Main (String[]args) {
F2 ();
}
that's pretty much with telling the compiler thatThe Fruitreader Readcovariant method accepts a parameter as long as it satisfies the subclass of the fruit (including the fruit itself), and the relationship between the class and the parent class is also associated.
Pecs Principles
we saw a similar<? Extendst> the use of it, we can get elements from the list, could we add elements to the list? Let's try it out:
Publicclass Genericsandcovariance {
Publicstaticvoid Main (string[] args) {
Wildcards allow covariance:
list<? Extends fruit> flist = newarraylist<apple> ();
Compile Error:can ' t add any type of object:
Flist.add (Newapple ())
Flist.add (Neworange ())
Flist.add (Newfruit ())
Flist.add (NewObject ())
Flist.add (NULL); Legal but uninteresting
We Know that itreturns at least Fruit:
Fruit f = flist.get (0);
}
}
The answer is no,The Java compiler does not allow us to do this, why? We might as well consider this problem from the compiler's point of view. Because of list< Extends fruit> flist itself can have many meanings:
list<? Extends fruit> flist = newarraylist<fruit> ();
list<? Extends fruit> flist = newarraylist<apple> ();
list<? Extends fruit> flist = newarraylist<orange> ();
· When we try to add an apple, Flist may point to New arraylist<orange> ();
· When we try to add an orange, flist may point to New arraylist<apple> ();
· When we try to add a fruit, this fruit can be any type of fruit, and flist may only want a certain type of fruit, the compiler will not recognize the error.
so for the realization of the<? The Extendst> collection class can only treat it as an out-of-producer (get) element, but not as a consumer to get an (add) element externally.
If we're going toWhat should the add element do? can use &LT;? Super t>:
Publicclass genericwriting {
Static list<apple>apples = Newarraylist<apple> ();
Static list<fruit>fruit = Newarraylist<fruit> ();
static <T> void Writeexact (list<t> List, T item) {
List.add (item);
}
Staticvoid F1 () {
Writeexact (Apples, New Apple ());
Writeexact (Fruit, New Apple ());
}
static <T> void Writewithwildcard (list<? super t> List, T item) {
List.add (item)
}
Staticvoid F2 () {
Writewithwildcard (Apples, New Apple ());
Writewithwildcard (Fruit, New Apple ());
}
Publicstaticvoid Main (string[] args) {
F1 (); F2 ();
}
}
so we can add elements to the container, but using theThe disadvantage of super is that we can not get the elements inside the container after, the reason is very simple, we continue to consider this problem from the compiler point of view, for LIST&LT; Super Apple> list, which can have several meanings:
list<? Super Apple> list = newarraylist<apple> ();
list<? Super Apple> list = newarraylist<fruit> ();
list<? Super Apple> list = newarraylist<object> ();
when we try to passWhen a list gets an apple, it may get a fruit, which can be fruit other types of fruit such as orange.
According to the above example, we can summarize a rule,"Producerextends, Consumer Super":
· "Producerextends" – if you need a read-only list, use it to Producet. Extends T.
· "Consumersuper" – if you need a write-only list and use it to Consumet, then use? Super T.
· If you need to read and write at the same time, then we can't use wildcards.
How to read a fewJava Collection Class source code, you can find that usually we will combine the two together, such as the following:
Publicclass Collections {
publicstatic <T> void Copy (LIST&LT;? super t> dest, list<? extends t> src) {
for (int i=0; i<src.size (); i++)
Dest.set (i, Src.get (i));
}
}
Type Erase
Perhaps the most troubling part of Java generics is type Erasure, especially for programmers with C + + experience. Type erasure means that Java generics can only be used for static type checking during compilation, and then the compiler generates code that erases the appropriate type information so that the JVM actually knows the exact type represented by generics at runtime. This is done because Java generics are introduced after 1.5, and in order to maintain a downward compatibility, only type erasure is allowed to be compatible with the previous non-generic code. For this, if you read the source code of the Java Collection framework, you can see that some classes do not actually support generics.
Having said so much, what does a generic erase mean? Let's take a look at the following simple example:
Publicclass node<t> {
Private T data;
Private node<t>next;
Public Node (T data, node<t> next)} this.data = data;
This.next = Next;
}
Public T GetData () {return data;}
// ...
}
after the compiler has done the appropriate type checking, actually the above code will actually be converted to the following time during the run:
Publicclass Node {
Private Object data;
Private Node Next;
Public node (Object data, node next) {
This.data = data;
This.next = Next;
}
Public Object GetData () {return data;}
// ...
}
This means that no matter what we declare,Node<string> or node<integer>, the JVM is considered node<object> during the run. Is there any way to solve this problem? This will require us to reset the bounds, and change the code above to the following:
Publicclass Node<t extends Comparable<t>> {
Private T data;
Private node<t>next;
Public Node (T data, node<t> next) {
This.data = data;
This.next = Next;
}
Public T GetData () {return data;}
// ...
}
this way the compiler willThe place where T appears is replaced with comparable instead of the default Object:
Publicclass Node {
private comparable data;
Private Node Next;
Public node (comparable data, node next) {
This.data = data;
This.next = Next;
}
Public comparable GetData () {return data;}
// ...
}
The above concepts may be better understood, but in fact, the problem of generic erasure is far more than this, we will take a systematic look at the type of erasure caused by some problems, some problems inC + + generics may not be met, but in Java it requires extra care.
Question one
in theCreating generic arrays is not allowed in Java, and the compiler will give you an error like this:
list<integer>[] arrayoflists = new list<integer>[2]; Compile-time Error
Why does the compiler not support this approach? Continue to use reverse thinking, we stand in the compiler's perspective to consider this problem.
Let's take a look at the following example:
Object[] strings = new STRING[2];
Strings[0] = "HI"; Ok
STRINGS[1] = 100; Anarraystoreexception is thrown.
for this piece of code is still well understood that the string array can not hold integer elements, and such errors often wait until the code is running to find that the compiler is not recognized. Now let's look at the hypothesisWhat happens when Java supports the creation of generic arrays:
object[] Stringlists = newlist<string>[]; Compiler error, but pretend it ' s allowed
Stringlists[0] = newarraylist<string> (); Ok
An arraystoreexception should is thrown, but the runtime can ' t detectit.
STRINGLISTS[1] = newarraylist<integer> ();
Suppose we support the creation of a generic array, because the run-time type information has been erased,The JVM doesn't actually know the difference between New arraylist<string> () and new arraylist<integer> (). Errors such as these can be very difficult to detect if they occur in the actual application scenario.
If you have any doubts about this, try running the following code:
Publicclass Erasedtypeequivalence {
Publicstaticvoid Main (string[] args) {
Class C1 = new arraylist<string> (). GetClass ();
Class C2 = new arraylist<integer> (). GetClass ();
SYSTEM.OUT.PRINTLN (C1 = = C2); True
}
}
question two
and continue to reuse ourNode's class, for generic code, the Java compiler will actually secretly help us implement a bridge method.
Publicclass node<t> {
public T data;
Public Node (T data) {this.data = data;}
Publicvoid setData (T data) {
System.out.println ("Node.setdata");
This.data = data;
}
}
Publicclass Mynode extends Node<integer> {
Public Mynode (Integer data) {super (data);}
Publicvoid SetData (integerdata) {
System.out.println ("Mynode.setdata");
Super.setdata (data);
}
}
after reading the above analysis, you may think that after the type erase, the compiler willnode and Mynode become the following:
Publicclass Node {
public Object data;
Public Node (Object data) {this.data = data;}
Publicvoid SetData (objectdata) {
System.out.println ("Node.setdata");
This.data = data;
}
}
Publicclass Mynode extends Node {
Public Mynode (Integer data) {super (data);}
Publicvoid SetData (integerdata) {
System.out.println ("Mynode.setdata");
Super.setdata (data);
}
}

Students who want to learn the front-end development, can add group: 543#6273#93

Design 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.