Java generic (generics) is a new feature introduced in JDK 5. It allows you to use type parameter when defining classes and interfaces ). The declared type parameter is replaced by a specific type during use. The main application of generics is in the new collection class framework of JDK 5. The development community views on the introduction of generic concepts are mixed. In good terms, the introduction of generics can solve the common runtime type errors in the previous collection class framework during use, because the compiler can find many obvious errors at the time of compilation. To ensure compatibility with earlier versions, there are some not elegant implementations of Java generics. Of course, this is the historical burden of any historical programming language. Subsequent version updates will be tied to early design defects.
When using generics, developers can easily make some mistakes based on their instincts. For example, if a method receives list <Object> As a form parameter, if it attempts to pass in a list <string> object as an actual parameter, it finds that compilation fails. Although, intuitively, the object is the string parent class, this type conversion should be reasonable. But in fact this will generate implicit type conversion problems, so the compiler will directly prohibit such behavior. This article attempts to give a general description of Java generics.
Type Erasure
The primary prerequisite for a correct understanding of the generic concept is to understand the type erasure ). In Java, generics are basically implemented at the compiler level. The generated Java Byte Code does not contain type information in generics. The type parameters added when Using Generics will be removed by the compiler during compilation. This process is called type erasure. For example, the List <Object> and list <string> types defined in the Code will be changed to list after compilation. What JVM sees is only list, and the type information appended by generics is invisible to JVM. The Java compiler tries its best to discover possible errors during compilation, but it still cannot avoid type conversion exceptions at runtime. Type erasure is also an important difference between Java generic implementation and C ++ template implementation.
Many of the strange features of generics are related to the existence of this type of erasure, including:
- Generic classes do not have their own class objects. For example, there is no list <string>. Class or list <integer>. class, but only list. Class.
- Static variables are shared by all instances of the generic class. For Classes declared as myclass <t>, the method for accessing the static variables in the class is still myclass. mystaticvar. Objects Created through new myclass <string> or new myclass <integer> share a static variable.
- Generic Type parameters cannot be used in catch statements for Java exception handling. Because exception handling is performed by JVM at runtime. Because the type information is erased, JVM cannot distinguish two exception types: myexception <string> and myexception <integer>. For JVM, they are all of the myexception type. The catch statement corresponding to the exception cannot be executed.
The basic process of Type erasure is also relatively simple. First, find the specific class used to replace the type parameter. This class is generally an object. If the upper bound of the type parameter is specified, this upper bound is used. Replace all the type parameters in the Code with specific classes. Remove the type declaration that appears, that is, remove the content of <>. For example, the t get () method declaration changes to object get (); List <string> is changed to list. Next, you may need to generate some bridge methods ). This is because some necessary methods may be missing for the class after the type is erased. For example, consider the following code:
class MyString implements Comparable<String> { public int compareTo(String str) { return 0; }}
After the type information is erased, the declaration of the above class is changed to class mystring implements comparable. However, a compilation error occurs in the mystring class because the int compareto (object) method declared by the comparable interface is not implemented. This method is dynamically generated by the compiler.
Instance analysis
After learning about the type erasure mechanism, you will understand that the compiler undertakes all the type checks. The compiler prohibits certain generic usage to ensure the security of the type. The list <Object> and list <string> mentioned above are analyzed as follows:
1 Public void inspect (list <Object> List) {2 for (Object OBJ: List) {3 system. out. println (OBJ); 4} 5 list. add (1); // this operation is valid in the context of the current method. 6} 7 public void test () {8 list <string> STRs = new arraylist <string> (); 9 inspect (STRs); // compilation error 10}
In this Code, the inspect method accepts list <Object> as the parameter. When the test method tries to pass in list <string>, a compilation error occurs (in fact, if you input a list <classname>, a compilation error occurs, for example, list <integer> ). If this method is allowed, you can add a number to the set through list. Add (1) in the inspect method. In this way, in the test method, an integer type object is added to the set declared as list <string>. This apparently violates the type security principle and will certainly throw classcastexception at some time. Therefore, the compiler prohibits such behavior. The compiler checks possible type security issues as much as possible. If it is determined that this is in violation of relevant principles, a compilation error will be given. When the compiler cannot determine whether the type is used correctly, a warning is given.
Wildcard and Upper/Lower Bounds
When using generic classes, you can specify a specific type. For example, if list <string> is used, the specific type is declared as string. Can you also use wildcards? To represent unknown types, such as list <?> It is declared that the element types contained in the list are unknown. Wildcards represent a group of types, but the specific types are unknown. List <?> All declared types are acceptable. But list <?> It is not equivalent to list <Object>. List <Object> actually determines that the list contains the object and its subclass. You can reference the object when using it. And list <?> The element types contained in the table are uncertain. It may contain string or integer. If it contains a string, it is wrong to add an integer element to it. Because the type is unknown, you cannot use the new arraylist <?> () Method to create a new arraylist object. Because the compiler cannot know the specific type. But for list <?> Elements in can always be referenced by objects, because although the type is unknown, it must be an object and its subclass. Consider the following code:
Public void wildcard (list <?> List) {list. Add (1); // compilation error}
As shown above, a compilation error always occurs when you try to operate a wildcard class. The reason is that the type indicated by the wildcard is unknown.
Because list <?> The elements in can only be referenced by objects, which is not very convenient in some cases. In these cases, the upper and lower bounds can be used to limit the range of unknown types. Such as list <? Extends number> indicates that the element type in the list may be number and its subclass (Note: The number class is located in Java. in the lang Package, its direct subclasses include byte, double, float, integer, long, short, etc ). And list <? Super number> indicates that the list contains the number and its parent class. After the upper bound is introduced, you can use the method defined in the upper bound class when using the type. For example, access list <? Extends number>, you can use methods such as intvalue of the number class.
Type System
In Java, you are familiar with the type Architecture generated by the inheritance mechanism. For example, string is inherited from object. According to the liskov replacement principle, child classes can replace parent classes. When you need to reference the object class, it is no problem if you pass in a string object. However, when a subclass is referenced by a parent class, forced type conversion is required. The compiler does not guarantee that such conversions are valid at runtime. This automatic subclass replaces the type conversion mechanism of the parent class, which is also applicable to arrays. String [] can replace object []. However, the introduction of generics has a certain impact on this type of system. As mentioned above, list <string> cannot be replaced by list <Object>.
The type system added two dimensions after the introduction of generics: 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 parameter indicates that in the case of list <string> and list <Object>, the type parameter string is inherited from the object. The second type means that the List interface inherits from the collection interface. For this type of system, there are some rules as follows:
- The relationship between generic classes with the same type parameters depends on the inheritance architecture of the generic classes. That is, list <string> is the sub-type of collection <string>. List <string> can replace collection <string>. This situation also applies to type declarations with upper and lower bounds.
- When a wildcard is used in the type declaration of a generic class, its Subtypes can be expanded in two dimensions. For example, for collection <? Extends number>, its Subtypes can be expanded in the collection dimension, that is, list <? Extends number> and set <? Extends number> and so on. You can also expand the number hierarchy, that is, collection <double> and collection <integer>. As a result, arraylist <long> and hashset <double> are both collections. <? Child type of extends number>.
- If a generic class contains multiple type parameters, apply the preceding rules for each type parameter.
After understanding the above rules, you can easily correct the code given in the instance analysis. You only need to change list <Object> to list <?> You can. List <string> Yes list <?> So there is no error when passing parameters.
Develop your own generic classes
Generic classes are basically the same as general Java classes, but the type parameters declared by <> are added to the class and interface definitions. A class can have multiple type parameters, such as myclass <x, y, z>. You can specify an upper bound when declaring each type parameter. The declared type parameters can be used as method parameters and return values, or as the types of domain and local variables, just as common types in the Java class. However, due to the type erasure mechanism, type parameters cannot be used to create objects or as static variables. Consider the correct and incorrect usage in the following generic classes.
1 class classtest <X extends number, y, z> {2 private X; 3 Private Static y; // compilation error, cannot be used in static variables 4 Public x getfirst () {5 // correct usage 6 return x; 7} 8 Public void wrong () {9 z = new Z (); // compilation error, cannot create object 10} 11}
Best practices
Some basic principles can be followed when using generics to avoid some common problems.
- Avoid mixing generic classes and original types in code. For example, list <string> and list should not be used together. This will generate some compiler warnings and potential runtime exceptions. When you need to use the legacy code developed before JDK 5 and have to do so, try to isolate the relevant code as much as possible.
- When using generic classes with wildcards, you must specify the concept of a group of types represented by wildcards. Because the specific type is unknown, many operations are not allowed.
- It is best not to use the same Array for generic classes. You can only create a new list <?> [10] The new list <string> [10] cannot be created. This limits the ability to use arrays, and brings a lot of confusing problems. Therefore, you can use the collection class when you need functions similar to arrays.
- Do not ignore the warning information provided by the compiler.
Reprinted from: http://www.infoq.com/cn/articles/cf-java-generics