Java generics (generics) is a new feature introduced in JDK 5 that allows the use of type parameters (types parameter) when defining classes and interfaces. The declared type parameter is replaced with a specific type when used. The most important application of generics is in the new collection class framework in JDK 5. For the introduction of generic concepts, the development Community's view is mixed. On the good side, the introduction of generics can solve the runtime type errors that the previous collection class framework would normally have in use, because the compiler can find many obvious errors at compile time. In the never-so-good place, there are some less elegant implementations of Java generics to ensure compatibility with older versions. This is, of course, the historical burden of any historical programming language. Subsequent versions of the update will be tired of earlier design flaws.
When using generics, developers can easily make mistakes based on their intuition. For example, if a method receives list<object> as a formal parameter, if it tries to pass a List<string> object as an actual argument, it finds that it cannot be compiled. Although it is intuitively that object is the parent of string, this type conversion should be reasonable. In practice, however, this creates an implicit type conversion problem, so the compiler directly prohibits such behavior. This article attempts to make a general description of Java generics.
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 MyString implements comparable<string> {public int 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 (1); This operation is valid in the context of the current method. }public void Test () { list<string> STRs = 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) {
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.
Class Classtest<x extends number, Y, z> { private x x; private static y y; Compile error, cannot be used in static variable public x GetFirst () { //correct usage return X; } public void Wrong () { Z z = new Z ();//Compilation error, object cannot be created
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.
Citation: Http://www.infoq.com/cn/articles/cf-java-generics
Java Generic face questions
1. What are generics in Java? What are the benefits of using generics ?
This is one of the questions you'll be asked about in a variety of Java generic interviews, focusing primarily on beginner and mid-level interviews. Those with a Java1.4 or earlier development background know how inconvenient it is to store objects in a collection and type-convert them before they are used. Generics prevent that situation from happening. It provides type safety at compile time, ensuring that you can only put objects of the correct type into the collection, avoiding classcastexception at run time.
2. How does Java generics work? What is type erase?
This is a better question for a generic face. Generics are implemented by type erasure , and the compiler erases all type-related information at compile time, so there is no type-related information at run time. For example, the list is represented by a list at run time. The purpose of this is to make sure that the Binary class library is compatible with the version developed prior to Java 5. You cannot access the type parameter at run time because the compiler has converted the generic type to the original type . Depending on your answer to this generic question, you'll get some follow-up questions, such as why generics are implemented by type Erasure or show you some of the wrong generic code that causes the compiler to make an error. Please read how generics in my Java work to learn more.
3. What are qualified wildcard and unqualified wildcard characters in generics?
This is another very popular Java generic surface question. Restrictions on the type are limited by wildcard characters. There are two qualifying wildcards, one of which is to set the upper bounds of the type by ensuring that the type must be a subclass of T, and the other is to set the lower bound of the type by ensuring that the type must be the parent class of T. A generic type must be initialized with a qualified type, or it will result in a compilation error. On the other hand, the unqualified wildcard is represented, because any type can be substituted. For more information, see the differences between qualifying wildcards and unqualified wildcards in my article generics.
4. What is the difference between list and list?
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 examples of qualifying wildcards, and list can accept any list of types that inherit from T, and List can accept a list of any of the parent classes of T. For example, listextendsnumber> can accept list or list. 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:
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. Of course, if you've already written a junit test that you can run, you can write your own implementation code at your own discretion.
8. Can you pass the list to a method that accepts the list parameter?
For anyone unfamiliar with generics, this Java generic topic looks confusing because at first glance the string is an object, so the list should be used where the list is needed, but that's not the case. If you do this, you will cause a compilation error. If you take it one step further, you'll find that Java does this to make sense, because list can store any type of object including string, Integer, and so on, and list is only used to store strings.
List<object> objectList; List<string> stringlist; Bjectlist = 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, Java 5 's Javac compiler produces warnings that are not checked by the type, such as
list<string> rawlist = new ArrayList () Note: Hello.java uses an operation that is not checked or is called unsafe;
This warning can be masked using @suppresswarnings ("unchecked") annotations.
java Generic surface question supplement update:
I've got a couple of Java generics. Questions to share with you, these questions focus on the difference between a generic type and a primitive type, and whether we can use object instead of qualifying wildcard usage, and so on:
in Java What is the difference between list and primitive type list?
The main difference between primitive and parametric types 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 type, 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 type with a parameter to the original type list, but you cannot pass the list to the method that accepts the list, because a mutation error occurs. For more details, see how generics work in Java.
list and in Java What is the difference between lists ?
This question looks very much like the previous question, but it is fundamentally different. List is an unknown type of list, and list is actually any type of list. You can assign list, list<integer> to list, but you can't assign a list to list<object< span= "" >>.
List<?> Listofanytype; list<object> listofobject = new arraylist<object> (); list<string> listofstring = new arraylist<string> (); list<integer> Listofinteger = new arraylist<integer> (); Listofanytype = listofstring; Legallistofanytype = Listofinteger; Legallistofobjecttype = (list<object>) listofstring; Compiler error-in-convertible Types
For more information on wildcards see the generic wildcard example in Java
the difference between the list 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.
List listofrawtypes = new ArrayList (); Listofrawtypes.add ("abc"); Listofrawtypes.add (123); The compiler allows this-the runtime to appear with an exception String item = (string) listofrawtypes.get (0); An explicit type conversion is required for item = (String) listofrawtypes.get (1); Throw classcastexception because the integer cannot be converted to String list<string> listofstring = new ArrayList (); Listofstring.add ("ABCD"); Listofstring.add (1234); Compile error, better than throw exception at runtime item = listofstring.get (0); No explicit type conversions are required-compiler auto-conversion
2.java Generic Foundation