Turn from:generics in Java are type-erased
The introduction of Java Generics (Generic) reinforces the security of parameter types and reduces the conversion of types, but one thing to note: Java generics are valid in compilers, deleted at run time, that is, all generic parameter types are erased after compilation, looking at the following column sub, the code is as follows:
Public class Foo { publicvoid listmethod (list<string> stringlist) { } Public void Listmethod (list<integer> intlist) { } }
The code is simple and seems to be fine, but the compiler is reporting the following error message:
Method Listmethod (list<string>) have the same erasure Listmethod (list<e>) as another method in type Foo
This error means that the Listmethod (list<string>) method is Listmethod (list<e>) after erasing the type at compile time, which is repeated with another method, that is, the method signature is duplicated. The following is the method code after the decompile:
Public void Listmethod (List list) { }
From the above code can be seen in the Java compiled bytecode has no generic information, after the compilation of all generic types will do the corresponding conversion, converted as follows:
- The type of list<string>, List<t> Erase is List.
- List<string>[], list<t>[] Erase type is list[].
- list<? Extends e>, list<? The type of super e> erased is list<e>.
- List<t extends Serialzable & cloneable> erase type list<serializable>.
Why does Java handle this? There are two reasons for this:
- Avoid the JVM's big shake. If the JVM extends the generic type to run time, then the JVM will need to do a lot of refactoring to run, which will improve the efficiency of the run-time.
- Version compatible. Erasing at compile time can better support native types (raw type).
Understand that Java generics are type erase, the following question is well understood:
(1) The generic class object is the same
Each class has a class attribute, and generics do not change the return value of the Class property, for example:
Public Static void Main (string[] args) { Listnew arraylist<string>(); ListNew arraylist<integer>(); = = Li.getclass ()) ; }
The code returns a value of true for the simple reason that,list<string> and list<integer> erase types are List.
(2) Generic array initialization cannot declare a generic type
The following code compiles, however:
New list<string>[];
Here you can declare an array with a generic parameter, but you cannot initialize the array, because list<object>[] and list<string>[] are the same when the type erase operation is performed, and the compiler refuses to declare this.
(3) instanceof does not allow the existence of generic parameters
The following code cannot be compiled for the same reason that the generic type was erased.
New Arraylist<string>(); instanceof list<string>)
The error message is as follows:
Cannot perform instanceof check against parameterized type list<string>. Use the form list<?> instead since further generic type information'll be erased at runtime
The following transfers are:Java generics: Type Sassafras, template, and generic pass -through
Type Erase
The first prerequisite for a proper understanding of the generic concept is to understand type erasure (type erasure). Generics in Java are basically implemented at the compiler level. In the generated Java byte code, the type information in generics is not included. When you use generics, the type parameters are removed by the compiler at compile time. This process is called type erasure. Types such as list<object> and list<string>, as defined in the code, become lists after compilation. The JVM sees only the list, and the type information appended by generics is not visible to the JVM. The Java compiler will try to identify possible errors at compile time, but there is still no way to avoid a type conversion exception at run time. Type erasure is also an important difference between the generic implementation of Java and the way the C + + template mechanism is implemented.
Many of the strange features of generics are related to the existence of this type of erasure, including:
- A generic class does not have its own unique class object. For example, there is no list<string>.class or list<integer>.class, and only List.class.
- A static variable is shared by all instances of the generic class. For classes declared as Myclass<t>, the method of accessing static variables in them is still myclass.mystaticvar. Whether you create an object through new myclass<string> or new myclass<integer>, you are sharing a static variable.
- A generic type parameter cannot be used in a catch statement for Java exception handling. Because exception handling is performed by the JVM at run time. Because the type information is erased, the JVM cannot differentiate between the two exception types Myexception<string> and myexception<integer>. For the JVM, they are all myexception types. You cannot execute a catch statement that corresponds to an exception.
The basic process of type erasure is also relatively straightforward, first of all to find a specific class to replace the type parameter. This specific class is generally object. If the upper bound of the type parameter is specified, the upper bound is used. Replace the type parameters in the code with the specific classes. Remove the <> content by removing the type declaration that appears. For example, the t get () method declaration becomes an object get ();list<string> becomes a List. Next, you may need to generate some bridging methods (bridge method). This is because the class after the erasure type may be missing some of the necessary methods. Consider the following code, for example:
class Implements Comparable<string> { publicint compareTo (String str) { return 0;
When the type information is erased, the declaration of the above class becomes the class MyString implements comparable. However, the class mystring will have a compile error because there is no int compareTo (Object) method that implements the comparable declaration of the interface. This method is dynamically generated by the compiler at this time.
Example analysis
Once you understand the type erase mechanism, you will understand that the compiler takes all of the type checking work. The compiler prohibits the use of some generics precisely to ensure type security. Take the above mentioned list<object> and list<string> as an example to analyze specifically:
Public void Inspect (list<object> List) { for (Object obj:list) { System.out.println (obj); } List.add (//}publicvoid Test () { List New Arraylist<string>(); // }
In this code, the inspect method takes list<object> as a parameter, and a compilation error occurs when attempting to pass in the list<string> in the test method. Assuming this is permissible, the inspect method can add a number to the collection by List.add (1). This way, the test method appears to have an integer type Object added to the collection declared as list<string>. This is obviously a violation of the principle of type safety, and at some point it will definitely throw classcastexception. Therefore, the compiler prohibits such behavior. The compiler will check for possible types of security issues as much as possible. A compilation error is given where it is determined to violate the relevant principles. A warning message is given when the compiler cannot determine whether the type is being used correctly.
wildcard characters and upper and lower bounds
When using a generic class, you can specify either a specific type, such as list<string> that the specific type is String, or a wildcard to represent an unknown type, such as list<?>, which declares that the element type contained in the list is unknown. Wildcard characters are actually a set of types, but the specific type is unknown. What list<?> is declaring is that all types are possible. But list<?> is not the same as list<object>. List<object> actually determines that the list contains object and its subclasses, which can be referenced by object when used. List<?>, however, contains an indeterminate element type. This may contain either a string or an Integer. If it contains a string, it is wrong to add an element of type integer to it. Just because the type is unknown, you cannot create a new ArrayList object by means of new arraylist<?> (). Because the compiler cannot know what the specific type is. However, the elements in list<?> can always be referenced with object, because although the type is unknown, it must be object and its subclasses. Consider the following code:
Public void wildcard (list<?> List) { list.add (1); //
As shown above, there is always a compile error when trying to manipulate a generic class with wildcard characters. The reason for this is that the type represented by the wildcard is unknown.
Because the elements in list<?> can only be referenced by object, in some cases it is not very convenient. In these cases, you can use the upper bound to limit the range of unknown types. such as List<? Extends number> indicates that the type of element that may be contained in the list is number and its subclasses. and list<? The super number> indicates that the list contains number and its parent class. When an upper bound is introduced, the method defined in the upper bound class can be used when the type is used. such as access to list<? When extends number>, you can use methods such as the number class Intvalue.
Type System
In Java, you are more familiar with the type architecture that is generated by the inheritance mechanism. For example, string inherits from object. Depending on the Liskov substitution principle, subclasses can replace the parent class. When a reference to the object class is required, there is no problem with passing in a string object. However, when you replace a subclass reference with a reference to a parent class, you need to cast the coercion type. The compiler does not guarantee that this conversion must be legal at runtime. This automatic subclass replaces the parent class's type conversion mechanism, which is also applicable for arrays. String[] can be replaced by object[]. However, the introduction of generics has a certain impact on this type of system. As mentioned above, list<string> cannot replace list<object>.
The type system introduced after generics adds two dimensions: one is the inheritance architecture of the type parameter itself, and the other is the inheritance architecture of the generic class or interface itself. The first one refers to cases such as list<string> and list<object>, where the type parameter String is inherited from Object. The second means that the list interface inherits from the collection interface. For this type of system, there are some rules:
- The relationship of a generic class of the same type parameter depends on the inheritance architecture of the generic class itself. That is, list<string> is a collection<string> subtype,list<string> can replace collection<string>. This also applies to type declarations with upper and lower bounds.
- When a wildcard character is used in the type declaration of a generic class, its subtypes can be expanded separately on two dimensions. As to Collection< Extends number>, its subtypes can be expanded in the dimension of collection, that is, list< Extends number> and set<? Extends number>, etc., can also be expanded at the number level, namely collection<double> and collection<integer>. So the cycle down,arraylist<long> and hashset<double>, etc are also considered collection< Extends subtype of number>.
- If a generic class contains more than one type parameter, the above rule is applied separately for each type parameter.
After understanding the above rules, it is easy to correct the code given in the example analysis. Just change the list<object> to list<?>. List<string> is a subtype of list<?>, so no error occurs when passing parameters.
Develop your own generic classes
Generic classes are basically the same as General Java classes, except for the type arguments that are declared with <> on the class and interface definitions. A class can have more than one type parameter, such as Myclass<x, Y, z>. Each type parameter can specify an upper bound at the time of declaration. The declared type parameter can be the same as the normal type in the Java class as the parameter and return value of the method, or as the type of the domain and local variables. However, because of the type erasure mechanism, type parameters cannot be used to create objects or as types of static variables. Consider the correct and incorrect usage in the following generic class.
classClasstest<xextendsNumber, Y, z> { Privatex x; Private StaticY y;//compile error, cannot be used in static variable PublicX GetFirst () {//Correct usage returnx; } Public voidwrong () {Z Z=NewZ ();//compile error, cannot create object }}
Generic Pass-through
That is, generics can be passed as parameters in different instantiated classes, which can theoretically be passed indefinitely. Ultimately, the method or type of generic determination for each layer is constrained, and the specific usage is described in detail in the generic pass-through article.
Best practices
There are some basic principles that you can follow when using generics to avoid some common problems.
- Avoid mixing generic classes and primitive types in your code. For example, list<string> and list should not be used together. This produces some compiler warnings and potential run-time exceptions. When you need to take advantage of legacy code developed prior to JDK 5 and have to do so, isolate the relevant code as much as possible.
- When using a generic class with wildcard characters, you need to clarify the concept of a set of types represented by wildcards. Because the specific type is unknown, many operations are not allowed.
- Generic classes are best not to be used with arrays. You can only create an array such as new list<?>[10], and you cannot create new list<string>[10] like this. This limits the ability of arrays to be used, and it can cause a lot of puzzling problems. Therefore, when a function like an array is required, the collection class can be used.
- Do not ignore the warning messages given by the compiler.
Generic type Erasure in Java