Java generics in-depth understanding

Source: Internet
Author: User
Tags readable



Pre-generics


In object-oriented programming language, polymorphism is a generalization mechanism. For example, you can set the parameter type of a method to a base class, and the method can accept any class derived from the base class as a parameter, which would be more versatile. In addition, if you declare a method parameter as an interface, you will be more flexible.

Before Java adds generic types, the design of a generic program is implemented using inheritance, for example, the ArrayList class maintains only an array of object references, and object is the base class for all classes.


[Java] View plain copy
public class Beforegeneric {
General program design prior to static class arraylist{//generics
Private object[] Elements=new object[0];
Public Object get (int i) {
return elements[i];
}
public void Add (Object o) {
The implementation here, just for demonstration, does not have any reference value
int length=elements.length;
Object[] Newelments=new object[length+1];
for (int i=0;i<length;i++) {
Newelments[i]=elements[i];
}
Newelments[length]=o;
elements=newelments;
}
}
public static void Main (string[] args) {
ArrayList stringvalues=new ArrayList ();
Stringvalues.add (1);//You can add any type of object to the array
The problem must be cast when getting a value
String str= (String) stringvalues.get (0);
Problem 2--The above forced transformation compiles without errors, and the Run Times exception java.lang.ClassCastException
}
}
This implementation faces two problems:

1. When we get a value, we must make a forced type conversion.

2. Suppose we expect to use Stringvalues to hold the string collection, because ArrayList only maintains an array of object references, we cannot prevent the addition of data of the integer type (object subclass) to Stringvalues. However, when we use the data, we need to convert the acquired object to the type we expect (String), and if we add an unintended type (such as an integer) to the collection, we will not receive any error prompts at compile time. But when we run the program, we report an exception:

Exception in thread "main" Java.lang.ClassCastException:java.lang.Integer cannot is cast to java.lang.String
at generic. Beforegeneric.main (beforegeneric.java:24)

This is obviously not what we expected, and if the program has a potential error, we expect to be told the error at compile time instead of running the Times exception.

Generic type

Generics provide a better solution to the problem of using inheritance to achieve universal programming: type parameters. For example, the ArrayList class uses a type parameter to indicate the type of the element.

[Java] View plain copy
Arraylist<string> stringvalues=new arraylist<string> ();
Such code is more readable, and we know that the collection is used to hold the object of type string, rather than just relying on the variable name to imply the type we expect.

[Java] View plain copy
public class GenericType {
public static void Main (string[] args) {
Arraylist<string> stringvalues=new arraylist<string> ();
Stringvalues.add ("str");
Stringvalues.add (1); Compile error
}
}
Now, if we add an object of type integer to Arraylist<string>, a compilation error will occur.
Exception in thread "main" java.lang.Error:Unresolved compilation problem:
The method Add (int, String) in the type arraylist<string> are not applicable for the arguments (int)
at generic. Generictype.main (Generictype.java:8)

The compiler automatically checks for us to avoid inserting the wrong type of object into the collection, which makes the program more secure.

In short, generics make our programs more readable and secure through type parameters.


How Java generics are implemented

Erase


[Java] View plain copy
public class GenericType {
public static void Main (string[] args) {
Arraylist<string> arraystring=new arraylist<string> ();
Arraylist<integer> arrayinteger=new arraylist<integer> ();
System.out.println (Arraystring.getclass () ==arrayinteger.getclass ());
}
}
Output:
True

In this example, we define two ArrayList arrays, but one is a arraylist<string> generic type and can only store strings. One is a arraylist<integer> generic type and can only store integral types. Finally, we obtain their class information by Arraystring objects and the GetClass method of the Arrayinteger object and compare them, and find the result to be true.

What is this for, obviously we define two different types? Because, during compilation, all generic information is erased by the,list<integer> and list<string> types, and after compilation it becomes the List type (the original type). Generics in Java are basically implemented at the compiler level, which is why Java generics are called Pseudo generics.

Original type

The original type is the true type in the bytecode after the generic type erases the generic information. Whenever a generic type is defined, the corresponding primitive type is automatically provided. The name of the original type is the class name of the generic type after the type parameter is deleted. Erases the type variable and replaces it with a qualified type (T is an unqualified type variable, replaced with object).

[Java] View plain copy
//generic type
class Pair<t> {
Private T value;
Public T GetValue () {
return value;
}
public void SetValue (T value) {
This.value = value;
}
}
[Java] View plain copy
//original type
class Pair {
Private Object value;
Public Object GetValue () {
return value;
}
public void SetValue (Object value) {
This.value = value;
}
}
Because T is an unqualified type variable in pair<t>, it is replaced with object. If it is pair<t extends Number>, after erasing, the type variable is replaced with the number type.


[Java] View plain copy
public class Reflectingeneric {
public static void Main (string[] args) throws IllegalArgumentException,
SecurityException, Illegalaccessexception, InvocationTargetException, nosuchmethodexception {
Arraylist<integer> array=new arraylist<integer> ();
Array.add (1);//This calls the Add method to store only shaping, because an instance of a generic type is an integer
Array.getclass (). GetMethod ("Add", Object.class). Invoke (Array, "ASD");
for (int i=0;i<array.size (); i++) {
System.out.println (Array.get (i));
}
}
}
Output:

1
Asd

Why is it? When we introduce generics, we indicate that we insert a string type object into the arraylist<integer>, and we get an error at compile time. Now why is it possible again?

We define a arraylist<integer> generic type in the program, and if you call the Add method directly, you can store only the shaping data. But when we call the Add method with reflection, we can store the string. This means that arraylist<integer> generic information is erased after compilation, only the original type is preserved, the type variable (T) is replaced with object, and at run time, we can insert any type of object into the line.

However, it is not recommended to manipulate generic types in this way, as this violates the original intention of generics (reducing forced type conversions and ensuring type safety). When we get an element from the collection, by default the object is cast to the type specified by the generic parameter (this is an integer), and an exception occurs if an illegal object is put into the cast.

Type inference for generic methods
When you call a generic method, you can either specify a generic type or do not specify it.

In cases where a generic type is not specified, the generic type is the minimum level of the common parent class for several parameter types in the method, until object.

When specifying a generic type, all parameter types in the method must be of that generic type or its subclasses.


[Java] View plain copy
public class Test {
public static void Main (string[] args) {
/** do not specify a generic type */
int I=test.add (1, 2); Both of these parameters are integers, so t is replaced with an integer type
Number F=test.add (1, 1.2);//The two parameters are an integer and the other is float, so take the minimum level of the same parent class as number
Object O=test.add (1, "ASD");//The two parameters are an integer and the other is a string, so take the minimum level of the same parent class as Object

/** when specifying a generic type */
int A=test.<integer>add (1, 2);//Specifies an integer, so only the integer type or its subclasses
int B=test.<integer>add (1, 2.2);//compilation error, specified Integer, cannot be float
Number C=test.<number>add (1, 2.2); is specified as number, so it can be integer and float
}

This is a simple generic method
public static <T> t Add (t x,t y) {
return y;
}
}
The right operation

Now that the type variable is erased at compile time, why is the arraylist<integer> generic type defined and not allowed to be inserted into the string object? Not that the generic variable integer will erase to the original type object at compile time, why can't I store another type? Since the type is erased, how do we guarantee that we can only use generic variable-qualified types?
How does Java solve this problem? The Java compiler is compiled by first checking the type of the generic in the code, then the type erasure. Take the following code as an example:

[Java] View plain copy
Pair<integer> pair=new pair<integer> ();
Pair.setvalue (3);
Integer Integer=pair.getvalue ();
System.out.println (integer);
Erasing the return type of GetValue () returns the object type, which is automatically inserted by the compiler into the forced type conversion of the integer. That is, the compiler translates this method call into a two-byte code directive:
1. Calls to the original method Pair.getvalue

2. Cast the returned object type to Integer

In addition, when you access a generic field, you also insert a forced type conversion. Therefore, we say that Java generics are implemented at the compiler level, known as pseudo generics, relative to C + +.

Generic-related issues

1. Generic type reference pass-through problem

In Java, reference passing, like the following form, is not allowed:


[Java] View plain copy
Arraylist<string> arraylist1=new arraylist<object> ();//Compilation error
Arraylist<object> arraylist1=new arraylist<string> ();//Compilation error
Let's look at the first case and extend the first case into the following form:

[Java] View plain copy
arraylist<object> arraylist1=new arraylist<object> ();
Arraylist1.add (New Object ());
Arraylist1.add (New Object ());
arraylist<string> arraylist2=arraylist1;//Compilation error
In fact, there will be a compilation error at the 4th line of code. Well, let's just assume that it compiles correctly. So when we use the ARRAYLIST2 reference to take a value with the Get () method, the object returned is a string type, but it is actually already stored in the object type objects, so there will be classcastexception. So in order to avoid this very prone error, Java does not allow such a reference to be passed. (This is also the reason why generics occur, that is, to solve the problem of type conversion, we can not violate its original intention).
in the second case, expand the second case to the following form:

[Java] View plain copy
arraylist<string> arraylist1=new arraylist<string> ();
Arraylist1.add (New String ());
Arraylist1.add (New String ());
arraylist<object> arraylist2=arraylist1;//Compilation error
Yes, it's better than the first case, at least, The classcastexception is not present when we use the ArrayList2 value, because it is converted from string to object. However, what is the point of this, the reason why generics occur is to solve the problem of type conversion. We used generics, and in the end, we had to turn our own strong, violating the original purpose of the generic design. So Java is not allowed to do so. Besides, if you use ArrayList2 to add a new object to the inside, then how do I know if I'm taking out the string type or object type when I get there?
so pay extra attention to the reference delivery problem in generics.
2, a generic type variable cannot be a basic data type
For example, there is no arraylist<double> Because when a type is erased, the type variable (T) in the original class of ArrayList is replaced with object, but the object type cannot store a double value.
3, run-time type query
For example:

[Java] View plain copy
Arraylist<string> arraylist=new arraylist<string> ();
Because,arraylist<string> only has the original type after the type erase, the generic information String does not exist. Then, it is wrong to use the following method when the runtime makes a type query
[Java] View plain copy
if (arrayList instanceof arraylist<string>)
How does Java qualify this type of query? is a wildcard character, which is also a non-qualifier.
[Java] View plain copy
if (arrayList instanceof arraylist<?>)
4. Problems of generics in static and static classes
Static and static variables in generic classes cannot use generic type parameters declared by a generic class
[Java] View plain copy
public class Test2<t> {
public static T one; Compile error
public static T Show (T one) {//Compile error
return null;
}
}
Because the instantiation of a generic parameter in a generic class is specified when a generic type object (such as arraylist<integer>) is defined, static variables and static methods do not need to be invoked using an object. object is not created, how to determine what type of generic parameter it is, so of course it is wrong.
However, it is important to distinguish between the following situations:
[Java] View plain copy
public class Test2<t> {
public static <t >t Show (T one) {//This is correct
return null;
}
}
Because this is a generic method, the t used in a generic method is the t that you define in the method, not the generic class.

Question of generic related face

1. What are generics in Java? What are the benefits of using generics?
Generics are a mechanism for parameterized types. It allows code to be applied to various types, thus writing more generic code, such as a collection framework.

Generics are a compile-time type acknowledgement mechanism. It provides type safety at compile time, ensuring that only the correct type of objects can be used on a generic type (typically a generic collection), avoiding classcastexception at run time.

2. How does the Java generics work? What is type erase?
The normal work of generics is to rely on the compiler when compiling the source code, the type check first, then the type erasure and the type parameter where the occurrence of the insertion of the relevant instructions implementation of the cast.

The compiler erases all type-related information at compile time, so there is no type-related information at run time. For example, list<string> is represented only by a list type at run time. Why do you want to erase it? This is to avoid type bloat.

3. What are qualified wildcard and unqualified wildcard characters in generics?
Restrictions on the type are limited by wildcard characters. There are two kinds of qualified wildcard characters, one is < Extends t> it sets the upper bounds of the type by ensuring that the type must be a subclass of T, and the other is < Super t> It sets the lower bound of a type by ensuring that the type must be the parent of T. A generic type must be initialized with a qualified type, or it will result in a compilation error. On the other hand, <?> represents an unqualified wildcard, because <?> can be substituted for any type.

4. list<? Extends t> and list <? What is the difference between Super t>?
This is related to the previous interview question, and sometimes the interviewer will use it to evaluate your understanding of generics, rather than asking you directly what qualifies wildcards and unqualified wildcards. These two List declarations are all examples of qualifying wildcard characters, LIST< Extends t> can accept any list of types that inherit from T, and list<? Super T> can accept a list of any of the parent classes of T. e.g. list<? Extends number> can accept list<integer> or list<float>. More information can be found in the connection that appears in this section.

5. How do I write a generic method that can accept generic parameters and return generic types?
Writing a generic method is not difficult, you need to replace the original type with a generic type, such as using a widely recognized type placeholder such as T, E or K,V. For an example of a generic method, see the Java Collection Class framework. In the simplest case, a generic method might look like this:


[Java] View plain copy
Public V put (K key, V value) {
Return Cache.put (key, value);
}
6. How do I use generics to write classes with parameters in Java?
This is an extension of the last interview question. The interviewer may ask you to write a type-safe class with generics, rather than writing a generic method. The key is still to use generic types instead of the original types, and to use the standard placeholders that are used in the JDK.
7. Write a generic program to implement LRU caching?
This is the equivalent of an exercise for people who like Java programming. To give you a hint, linkedhashmap can be used to implement a fixed-size LRU cache, which moves the oldest key-value pairs out of the cache when the LRU cache is full. Linkedhashmap provides a method called Removeeldestentry (), which is used by the put () and Putall () calls to delete the oldest key-value pairs.
8. Can you pass the list<string> to a method that accepts list<object> parameters?
For anyone unfamiliar with generics, this Java generic topic looks confusing because at first glance the String is an Object, so list<string> should be used where it needs to be list<object>, But that is not the case. If you do this, you will cause a compilation error. If you take this one step further, you will find that Java does this to make sense, because list<object> can store objects of any type, including String, Integer, and so on, while list<string> can only be used to store strings.

[Java] View plain copy
List<object> objectList;
List<string> stringlist;

ObjectList = stringlist;//compilation error incompatible types
9. Can I use generics in array?
This is probably the simplest of the Java generic interview questions, but if you want to know that the array does not actually support generics, that's why Joshua Bloch suggests using list instead of array in the effective Java book. Because list can provide a type-safety guarantee for the compile period, array does not.
10. How do I block a warning from a type that is not checked in Java?
If you mix generics with primitive types, such as the following code, the Javac compiler for Java 5 produces warnings that are not checked by type
, such as list<string> rawlist = new ArrayList ()
Note: Hello.java uses an operation that is not inspected or is called unsafe;
This warning can be masked using @suppresswarnings ("unchecked") annotations.
11, what is the difference between the list<object> and the original type list in Java? The main difference between the
primitive type and the parameter type <Object> is that at compile time the compiler does not type safety checks on the original type, but checks the type with the parameter, and by using object as the type, you can tell the compiler that the method can accept objects of any kind , such as String or integer. The point of this problem is the correct understanding of the primitive types in generics. The 2nd difference between them is that you can pass any generic type with parameters to the method that accepts the original type list, but you cannot pass list<string> to the method that accepts List<object>, because a compilation error occurs.

12. What is the difference between list<?> and list<object> in Java?
This question looks very much like the previous question, but it is fundamentally different. List<?> is an unknown type of list, and list<object> is actually any type of list. You can put List<string>, list<integer> assigned to List<?>, but not list<string> assigned to list<object>.


[Java] View plain copy
List<?> Listofanytype;
list<object> listofobject = new arraylist<object> ();
list<string> listofstring = new arraylist<string> ();
list<integer> Listofinteger = new arraylist<integer> ();

Listofanytype = listofstring; Legal
Listofanytype = Listofinteger; Legal
Listofobjecttype = (list<object>) listofstring; Compiler error-in-convertible Types
13, the difference between the list<string> and the original type List.
The question is similar to "what is the difference between a primitive type and a parameter type". With parameter types is type-safe, and its type safety is guaranteed by the compiler, but the original type list is not type-safe. You cannot put any other type of object other than string into a list of type string, and you can save any type of object in the original list. With a generic parameter type you do not need to do a type conversion, but for the original type you need an explicit type conversion.

[Java] View plain copy
List listofrawtypes = new ArrayList ();
Listofrawtypes.add ("abc");
Listofrawtypes.add (123); The compiler allows this-the runtime will have an exception
String item = (string) listofrawtypes.get (0); An explicit type conversion is required
Item = (String) listofrawtypes.get (1); Throw classcastexception, because integer cannot be converted to string

list<string> listofstring = new ArrayList ();
Listofstring.add ("ABCD");
Listofstring.add (1234); Compile errors, better than throwing exceptions at run time
item = listofstring.get (0); No explicit type conversions are required-compiler auto-conversion
Wildcard characters

Upper bound of wildcard characters

General use
[Java] View plain copy
public class Test {
public static void Printintvalue (LIST&LT;? extends number> List) {
for (number Number:list) {
System.out.print (Number.intvalue () + "");
}
System.out.println ();
}
public static void Main (string[] args) {
List<integer> integerlist=new arraylist<integer> ();
Integerlist.add (2);
Integerlist.add (2);
Printintvalue (integerlist);
List<float> floatlist=new arraylist<float> ();
Floatlist.add ((float) 3.3);
Floatlist.add ((float) 0.3);
Printintvalue (floatlist);
}
}
Output:
2 2
3 0
illegal use
[Java] View plain copy
public class Test {
public static void Fillnumberlist (LIST&LT;? extends number> List) {
List.add (new Integer (0));//Compilation error
List.add (New Float (1.0));//Compilation error
}
public static void Main (string[] args) {
list<? Extends number> list=new ArrayList ();
List.add (New Integer (1));//Compilation error
List.add (New Float (1.0));//Compilation error
}
}
list<? Extends number> can represent list<integer> or list<float> why can't you add an Integer or Float like that?
First, we know that list<integer> can only add integers. And the following code is possible:
[Java] View plain copy
list<? Extends number> list1=new arraylist<integer> ();
list<? Extends number> list2=new arraylist<float> ();
Assuming that the previous example does not compile the error, if we put the List1 or List2 into the method fillnumberlist, obviously there will be a type mismatch, the assumption is not true.
Therefore, we conclude that we cannot go to list< Add any object in extends t>, except NULL.

Then why the list<? Extends t> can iterate, because subclasses must have the same interface as the parent class, which is exactly what we expect.

wildcard character Lower bound

General use
[Java] View plain copy
public class Test {
public static void Fillnumberlist (LIST<? Super Number> List) {
List.add (new Integer (0));
List.add (New Float (1.0));
}
public static void Main (string[] args) {
list<? Super number> List=new ArrayList ();
List.add (New Integer (1));
List.add (New Float (1.1));
}
}
can add any subclass of number, why?
List< The super number> can represent List<t>, where T is the number parent class, although number does not have a parent class. If T is the parent of number, we would certainly be able to add a subclass of number to list<t>.
Illegal use
is not allowed for iterations of list<? supert>. Why is it? Do you know which interface to use to iterate the list? It is clear that only the object class interface can guarantee that the elements in the collection have the interface. Its application scenario is slightly.
Unbounded wildcard characters

Know the upper and lower bounds of the wildcard, in fact, it is also equivalent to know the unbounded wildcard, without any modification can be, a single "? ”。 such as List<?>, "? "Can represent any type," arbitrary "is an unknown type.
List<object> and list<?> are not the same,list<object> as list<?> subclasses. There is no way to add any objects to the list<?> list, except NULL.
General use
1, when the method is using the original object type as a parameter, as follows:
[Java] View plain copy
public static void Printlist (List<object> List) {
for (Object elem:list)
System.out.println (Elem + "");
System.out.println ();
}
You can choose to implement the following:
[Java] View plain copy
public static void Printlist (List<?> List) {
for (Object elem:list)
System.out.print (Elem + "");
System.out.println ();
}
This can be compatible with more output, not simply List<object>, as follows:
[Java] View plain copy
List<integer> li = arrays.aslist (1, 2, 3);
list<string> ls = arrays.aslist ("One", "one", "three");
Printlist (LI);
Printlist (LS);

Reference:

"Java Core technology Volume One"

http://blog.csdn.net/lonelyroamer/article/details/7868820

Http://www.oschina.net/translate/10-interview-questions-on-java-generics

Http://www.linuxidc.com/Linux/2013-10/90928.htm

Java generics in-depth understanding

Related Article

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.