Summary of basic problems of Java generics

Generics is a new feature of Java se 1.5. The essence of generics is parameterized types, that is, the data type operated is specified as a parameter. This parameter type can be used in the creation of classes, interfaces and methods, which are called generic classes, generic interfaces and generic methods respectively. The advantage of introducing generics into the Java language is security and simplicity.

Before Java se 1.5, when there was no generics, the "arbitrary" of parameters was realized by referring to the type object. The disadvantage of "arbitrary" was that it required explicit forced type conversion, which required developers to predict the actual parameter types. In case of cast errors, the compiler may not prompt errors, and exceptions occur only when running, which is a security risk.

The advantage of generics is to check type safety at compile time, and all coercions are automatic and implicit, so as to improve the reuse rate of code.

1. Type parameters of generic types can only be class types (including custom classes), not simple types.

2. The same generic can correspond to multiple versions (because the parameter type is uncertain), and different versions of generic class instances are incompatible.

3. A generic type can have more than one type parameter.

4. Generic parameter types can use extends statements, such as < T extends superclass >. It is customarily called "bounded type".

5. The parameter type of a generic type can also be a wildcard type. For example, class classType = Class. forName("java.lang.String");

Generic erasure and related concepts

Generics in Java are basically implemented at the compiler level. The generated Java bytecode does not contain type information in generics. The type parameters added when using generics will be removed by the compiler when compiling. This process is called type erasure.

Problems caused by type erasure and Solutions

1. First check, during compilation, and check the problems of compiled objects and reference passing

2. Automatic type conversion

3. Conflict between type erasure and polymorphism and its solution

4. A generic type variable cannot be a base data type

5. Runtime type query

6. Problems with using generics in exceptions

7. Array (this is not a problem caused by type erasure)

9. Conflicts after type erasure

10. Problems of generics in static methods and static classes

1. Question: what is the generic type of Java? What are the benefits and advantages? What are the differences between different versions of JDK generics?

A: genericity is a new feature of Java se 1.5. The essence of genericity is parameterized type. This parameter type can be used in the creation of classes, interfaces and methods, which are called generic classes, generic interfaces and generic methods respectively. Before Java se 1.5, when there was no generics, the parameter can only be arbitrary by referring to the type object. The disadvantage is that explicit forced type conversion is required, and this forced conversion is not checked at the compilation time, so it is easy to leave the problem to the runtime. The advantage of generics is to check the type safety at the compilation time, Moreover, all coercion is automatic and implicit, which improves the reuse rate of code and avoids ClassCastException at run time.

JDK 1.5 introduces generics to allow strong types to perform type checking at compile time; JDK 1.7 generic instantiation types have the ability of automatic inference, such as list < string > List = new ArrayList < string > (); It can be written as list < string > llist = new ArrayList < > (); In addition, JDK has the ability of automatic inference. The following expressions can be said to be compatible with different versions:

2. Question: how does Java generics work? What is type erase?

A: generics are implemented through type erasure. The compiler erases all information related to generic types during compilation, so there is no information related to generic types at runtime. For example, list < integer > is represented by only one list at runtime. The purpose of this is to be compatible with Java versions before 1.5. Generic erasure, specifically, is the first type check when compiling into bytecode, Then type erasure is performed (that is, all type parameters are replaced with their qualified types, including classes, variables and methods). Then, if the type erasure conflicts with polymorphism, a bridge method is generated in the subclass for resolution. Then, if the return type of the calling generic method is erased, a forced type conversion is inserted when calling the method.

3. Q: what is the difference between Java generic classes, generic interfaces and generic methods?

A: a generic class is a type that can only be determined when instantiating the object of the class. Its definition, such as class test < T > {}, must specify the specific type of generic T when instantiating the class.

Generic interfaces are the same as generic classes. For example, interface generator < E > {e dunc (e e e);}.

The class of the generic method can be generic or non generic. Whether the generic method is owned or not has nothing to do with the class. Therefore, we should use the generic method as much as possible in our application, and do not enlarge the scope. Especially when the static method is used, the static method cannot access the type parameters of the generic class, Therefore, generic static methods should be used (the declaration of generics must be written in front of the return value type after static). The definition of generic methods, such as < T > void func (t VAL) {}.

4. Question: how does Java gracefully implement tuples?

A: tuple is actually an academic term in relational database. A record is a tuple and a table is a relationship. Records form a table and tuples generate relationships. This is the core concept of relational database. Many languages naturally support tuples, such as python. In languages where the syntax itself supports tuples, tuples are represented by parentheses. For example, (int, bool, string) is a triple type, but it is more pitiful in languages such as Java and C. the language syntax itself does not have this feature. Therefore, if we want to implement tuples gracefully in Java, we can implement them with the help of generic classes, The following is an implementation of a triple type:

5. Question: what are the running results of the following program blocks and why?

A: the result of the above code segment is true, which is explained as follows.

Because the loaded file is the same class file, there is ArrayList Class file but no ArrayList < string > Class file, even through class The gettypeparameters () method can only obtain generic parameter placeholders like [t] when obtaining type information. Generics are implemented through erasure, so any specific generic types are erased after compilation (replaced with non Generic upper boundary, or object type if no boundary is specified). Generic types only appear during static type inspection, and the above are erased into ArrayList type, so the same class file is loaded at runtime.

6. Question: why should Java generics be implemented by erasure? What are the disadvantages or costs of erasure?

A: it can be said that the existence of Java generics is an inevitable compromise, which leads to the confusion of Java generics and even the failure of JDK generic design. The reason why Java implements the generic mechanism through erasure is actually for compatibility. Only in this way can the transformation process from non generic code to generic code be based on the implementation of existing class libraries. It is precisely because this compatibility also brings some costs, For example, generics cannot explicitly refer to operations of runtime types (such as up-down transformation, instanceof operation, etc.), because all information about parameters is lost, so whenever you use generics, you should remind yourself of the real erasure type behind them. In addition, erasure and compatibility lead to the fact that the use of generics is not mandatory (for example, list < string > List = new arraylist(); Equal writing); Secondly, erasure will lead us to be very cautious when writing code (for example, don't forget to add the upper boundary operation when you don't want to be erased as object type).

7. Q: are there any problems with the following three funcx methods, and why?

Answer: func1, func2 and func3 cannot be compiled.

Because generic erasure loses the ability to perform certain operations in generic code, any operation that needs to know the exact type information at run time will not work.

8. Q: is there a problem with the following code segment, what is the running effect and why?

ArrayList
arrayList = new ArrayList

();

arrayList. add(1);

arrayList. getClass(). getmethod("add",Object.class). invoke(arrayList,"abc");

for (int i=0; i

  System. out. println(arrayList.get(i));

}

A: because the ArrayList generic type defined in the program is instantiated as an object of integer, if you directly call the add method, you can only store shaping data. However, when we call the add method with reflection, you can store strings. Because the integer generic instance is erased after compilation and only the original type object is retained, it can be inserted naturally.

9. Q: please talk more deeply about your understanding of Java generic erasure and the problems it brings?

A: Java generics are pseudo generics because all generic information will be erased during compilation, For example, list < integer > is represented by only one list at runtime (therefore, we can add strings to integer's generic list by reflecting the add method, because they become objects after compilation). The purpose of this is to be compatible with Java versions before 1.5. Generic erasure is to first check the type and then erase the type when compiling into bytecode (that is, all type parameters are replaced with their qualified types, including classes, variables and methods. If the type variables are limited, the original type is replaced with the type of the first boundary, such as class PRD < T extensions comparable & serializable > {} The original type of the generic method is comparable). Then, if the type erasure conflicts with polymorphism, a bridge method is generated in the subclass for resolution. Then, if the return type of the calling generic method is erased, a forced type conversion is inserted when calling the method.

The type check of first check and then erase is for references. Calling a generic method with a reference will carry out type detection on the method called by the reference, regardless of the object it really refers to. It can be said that this is a problem caused by compatibility, as follows:

Therefore, the type check before erasure is for the reference. Calling a generic method with this reference will type check the method called by this reference, regardless of the object it really refers to.

Another problem caused by first checking and then erasing is that parameterized types in generics cannot support inheritance relations, because the original design intention of generics is to solve the disadvantages of object type conversion. If parameterized types in generics support inheritance operations, it violates the original design intention and continues to return to the disadvantages of original object type conversion. It can also be said that this is a problem caused by compatibility, as follows:

That's why we can prove it from the opposite side, Assuming that the compilation does not report an error, when the get () method is called through arraylist2, the object of type string is returned (because type detection is determined by reference), in fact, objects of object type are stored, so ClassCastException will be generated when getting it, which is contrary to the original intention of generics. For arraylist4, it is also assumed that the compilation does not report an error. When calling the get() method of arraylist4, the string obtained becomes object, although ClassCastException will not occur, But it still doesn't make sense. The reason why generics appear is to solve the problem of type conversion. Secondly, if we continue to add objects through the add () method of arraylist4, we can add object instances of any type, which will make us more confused when we get (), so it's an endless loop.

Another problem caused by erasure is the conflict between generics and polymorphisms. It solves the problem of polymorphic conflict by generating bridges in subclasses. The verification of this problem is also very simple, which can be illustrated by the following examples:

The operation of the above code segment is very surprised. Normally, after the creator class is compiled and erased, the parameter of setValue method should be of object type, and the parameter of setValue method of subclass stringcreator is of string type. It seems that this group of methods of parent and child classes should be overloaded, Therefore, it should be legal to call the setValue method of the subclass to add string and object type parameters. However, from the compilation point of view, the subclass does not inherit from the setValue method whose parent class parameter is object type, Therefore, the setValue method of a subclass overrides the parent class instead of overloading it (adding an @ override annotation from a subclass without an error can also explain the rewriting relationship). As for the principle of the above phenomenon, we can actually look at the compiled essence of the two classes through javap:

Through the compiled bytecode, we can see that the creator generic class is erased as object after compilation, and the original intention of our subclass is to rewrite to realize polymorphism. After the type is erased, the subclass conflicts with polymorphism, so a bridge method appears in the compiled bytecode to realize polymorphism. It can be seen that the parameter types of the bridge method are all object, that is, the bridge method really covers the parent method in the subclass, while the @ oveerride annotation on the subclass string parameter setValue and getValue methods is just an illusion. The internal implementation of the bridge method directly calls the two methods we have rewritten; However, the above setValue method is a bridge method generated to solve the conflict between type erasure and polymorphism, and getValue is a covariant, The reason why object getvalue() and string getvalue() methods in subclasses can exist at the same time is a distinction within the virtual machine (this is not allowed in our own code). Because a method signature is determined by the parameter type and return type inside the virtual machine, the compiler allows itself to do this seemingly illegal implementation in order to realize the polymorphism of generic types. In essence, it is left to the virtual machine to distinguish.

Another problem caused by checking before erasing is that automatic type conversion will be performed during generic reading. Therefore, if the return type of calling a generic method is erased, forced type conversion will be inserted when calling the method.

For this, you can check the bytecode instructions after using the add and get methods of list through javap. You will find that the checkcast instruction is not forced in the get method (although the return value in the get method is converted into t in the code, which is actually compiled and erased), but forced in the call.

Another problem caused by erasure is that generic type parameters cannot be basic types. For example, ArrayList < int > is illegal, and only ArrayList < integer > is legal, because after type erasure, the original type of ArrayList is object, and object is a reference type rather than a basic type.

Another problem caused by erasure is that runtime type checking of specific generic parameter types cannot be performed. For example, ArrayList instanceof ArrayList < string > is illegal. Java's support for generic runtime checking is limited to ArrayList instanceof ArrayList Way.

Another problem caused by erasure is that we cannot throw or capture objects of generic classes, because exceptions are captured and thrown at run time, and the generic information will be erased at compile time. After erasure, the two catches will become the same thing. Nor can generic variables be used in the catch clause, because generic information has been replaced with the original type at compile time (for example, catch (T) will become the original type throwable in the case of qualifier). If it can be used in the catch clause, it violates the exception capture priority order.

If you can explain this topic clearly and comprehensively, you will basically master 90% of generics.

10. Question: why can't Java generic arrays be initialized with specific generic types?

A: this problem can be illustrated by an example.

Due to the erasure mechanism of the JVM generic array, the above code can assign the value of OA [1] to ArrayList, and there will be no exception. However, when the data is fetched, there will be a type conversion, so ClassCastException will occur. If the generic array can be declared, there will be no warnings and errors during compilation, Errors occur only at runtime, but generics appear to eliminate ClassCastException. Therefore, if Java supports generic array initialization, it will be like lifting a stone and hitting yourself in the foot. It is true for the following code:

Therefore, it is allowed to initialize generic arrays by using wildcards, because the last data fetched by wildcards needs explicit type conversion, which is in line with the expected logic. Overview means that when initializing a generic array in Java, the array type cannot be a specific generic type, but can only be in the form of wildcards, because the specific type will lead to objects of any type that can be stored, and type conversion exceptions will occur when taking out, which will conflict with the design idea of generics, and the wildcard form needs to be forced by itself, which is in line with expectations.

For the answer to this question, the official Oracle document gives the reason: https://docs.oracle.com/javase/tutorial/extra/generics/fineprint.html

11. Q: which of the following statements are problematic and which are not?

A: the problem comment section of each statement above has explained that it is impossible to create an exact array of generic types in Java, unless wildcards are used and explicit type conversion is required.

12. Question: how to initialize generic array instances correctly?

A: no matter whether we initialize generic array instances in the form of new ArrayList [10] or generic wildcards, there are warnings, that is, only the syntax is qualified, and the potential risks at runtime need to be borne by ourselves. Therefore, initializing generic arrays in those ways is not the most elegant way, In the scenario of using generic arrays, we should try to use list collection replacement. In addition, we can also use Java lang.reflect. Array. Newinstance (class < T > componenttype, int length) method to create an array with the specified type and dimension, as follows:

Therefore, using reflection to initialize a generic array is an elegant implementation, because the generic type T can only be determined at run time. We must find a way to create a generic array at Java run time, and the best technology that can work at run time is reflection.

13. Q: can Java generic objects instantiate t = new t(), and why?

A: No, because the generic parameterization type cannot be determined during Java compilation, and the corresponding class bytecode file cannot be found, so it will not work. In addition, since t is erased as object, if new t() can be changed into new object(), it loses its original intention. If you want to instantiate a generic T, you can implement it through reflection (instantiating a generic array is similar), as follows:

The reason will not be explained. For the time being, we can think of creating a reason with the generic array above. As for the essential and deep-seated reasons, please pay attention to the push of generic reflection interview questions later.

14. Question: what are qualified and unqualified wildcards in Java generics? What's the difference?

A: qualified wildcards restrict types. There are two types of qualified wildcards in generic types. One is to ensure that the generic type must be a subclass of t to set the upper boundary of the generic type. The other is to ensure that the generic type must be the parent of t to set the lower boundary of the type. The generic type must be initialized with the type within the limit, otherwise it will lead to compilation errors. Unqualified wildcard It indicates that any generic type can be used to replace it. In a sense, it is the syntax format of generic upward transformation, because list < string > and list < Object > do not have inheritance relationship.

15. Q: what is the difference between list < Object > and the original type of list?

A: there are two main differences.

16. Q: briefly talk about list < Object > and list What is the difference between types?

A: this question looks very similar to the previous one, but it is completely different in essence. List It is an unknown type of list, and list < Object > is actually any type of list. We can assign list < string > and list < integer > to list , You cannot assign list < string > to list < Object >. For example:

Therefore, the form of wildcard can be replaced by the form of type parameter, and whatever wildcard can do can be done with type parameter. Wildcard form can reduce type parameters, which is often simpler in form and better readable. Therefore, wildcards can be used if they can be used. If there is a dependency between type parameters, or the return value depends on type parameters, or a write operation is required, only type parameters can be used.

17. Q: List and list ?

A: sometimes the interviewer will use this question to evaluate your understanding of generics instead of directly asking you what are qualified wildcards and unqualified wildcards. The declarations of these two lists are examples of qualified wildcards. List can accept any type of list inherited from t, and list can accept a list composed of any parent class of T. For example, list can accept list < integer > or list < float >. There are many such uses in the implementation of Java container classes. For example, there are the following methods in Collections:

18. Q: talk about < T extensions E > and ?

A: they are used in different places. < T extensions E > is used to define type parameters. It declares a type parameter t, which can be placed after the class name, interface and return value of generic methods in the generic class definition. is used to instantiate type parameters and type parameters in generic variables. However, the specific type is unknown and only knows that it is e or a subtype of E. Although they are different, they can often achieve the same purpose, such as:

19. Q: what is the relationship and difference between list < string > and list < Object >?

A: there is no relationship between the two things, only difference.

Because many people may think that string is a subclass of object, list < string > should be used where list < Object > is needed, but this is not the case. There is no inheritance relationship between generic types, so list < string > and list < Object > have no relationship and cannot be converted.

20. Q: are there any problems with the following two code fragments and why?

A: Part 1 compilation error, Part 2 compilation OK, operation error.

Because list < Object > and ArrayList < long > have no inheritance relationship, and Java arrays are type checked at run time.

21. Question: how to put int value into ArrayList < string > List = new ArrayList < string > (); In the list?

A: this question is essentially generic erasure, but there are many answers. One is through compatibility and the other is through reflection.

The following is achieved through generic erasure compatibility:

The following is achieved by reflection:

22. Q: what information is erased by generic erasure?

A: this question is more interesting and deep. Many people who don't have a deep understanding of generics may feel that there is a problem when they hear this question, because in their understanding, the generic information has been erased. How can they erase what information? Is there a situation? The answer is yes. Generic erasure is actually erasure by situation, not completely. We must eliminate this misunderstanding.

When compiling, Java will retain some generic information outside the instruction set in the bytecode. All generics on the definition of generic interfaces, classes and methods and the generics at the declaration of member variables will be retained, and the generic information in other places will be erased.

If you are interested, you can write generic code for various scenarios, then compile and decompile it to find it.

23. Q: since generic types are erased at compile time, how does a JSON parsing framework like gson parse data into a generic type bean structure?

A: in fact, this topic kills two birds with one stone, that is, it examines whether you are familiar with the gson framework, the relationship between Java generics and reflection, and the essence of generics.

Since the specific type of t cannot be known through getclass() during operation, gson solves this problem with the help of typetoken class. The example is as follows:

You can see that the use of typetoken is very simple. You can obtain the generic parameter type of the generic class we use by using the GetType () method only by constructing an anonymous subclass with the generic class that needs to obtain the type as the generic parameter of typetoken.

Through the above use example, we can find that there is no special construction method for beans using gson parsing transformation. Therefore, we can exclude the implementation scheme that the class type of the generic class passed in as the private attribute of the generic class in the construction method of the generic class to save the type information of the generic class, Therefore, through source code analysis, it is found that gson uses another way to obtain generic type parameters. Its method depends on the generic parameter information stored in Java's class bytecode. Although Java's generic mechanism is erased during compilation, the generic related information is saved in compiling java source code into class files, This information is stored in the constant pool of class bytecode. A signature field will be generated at the code that uses generics. The signature field indicates the address of the constant pool. JDK provides a method to read these generic information, and then the specific types of generic parameters can be obtained with the help of reflection. The specific implementation principle is as follows:

Therefore, the essence of obtaining generic parameter types is to return a parameterizedtype object through the getgenericsuperclass () method of class class (null is returned for object, interface and original type, and object. Class is returned for array class). Parameterizedtype represents Java type with generic parameter type. After JDK1.5 introduces generics, all classes in Java implement the type interface, parameterizedtype inherits the type interface, and all class classes containing generics will automatically implement this interface.

For more information about the generic parameter types stored in the class file, refer to: http://stackoverflow.com/questions/937933/where-are-generic-types-stored-in-java-class-files

24. Q: what is the output of the following program? Why?

A: the operation results are as follows.

Through the above example, we can find that each type parameter of a generic type is reserved and can be obtained through the reflection mechanism at run time, Because the generic erasure mechanism actually erases everything except structured information (structured information refers to the information related to the class structure, not the program execution process, that is, the metadata related to the type parameters of the class, its fields and methods will be retained and obtained through reflection).

25. Question: what are the execution results and reasons of the comment line in the following code fragment?

A: the execution of the comment line of the above code segment is explained as follows.

The three add methods are illegal. The compiler will report an error whether it is integer, number or object. Because? Indicates type safety ignorance,? Extensions number indicates that it is a subtype of number, but the specific subtype is unknown. If writing is allowed, Java cannot ensure type security, so it is directly prohibited.

The add of the last method is legal because form and on the contrary, the supertype wildcard represents a parent type of E. with it, we can write more flexibly.

This topic is particularly important: be sure to pay attention to generic type declaration variables? Rules for writing data when.

26. Question: what are the execution results and reasons of the comment line in the following code fragment?

A: the compilation and operation of the above code is shown in the notes. This topic mainly focuses on the "what is in generics"? The upper and lower boundary expansion of wildcards.

Wildcards have the following restrictions on the upper boundary: vector x = new vector < type 2 > (); If type 1 in specifies a data type, type 2 can only be type 1 or a subclass of type 1.

Wildcards have the following restrictions on the lower boundary: vector x = new vector < type 2 > (); If type 1 in specifies a data type, type 2 can only be type 1 or a parent of type 1.

27. Q: is the following procedure legal?

A: there is an error when compiling, because the Java type parameters are limited to the form of extensions, not super.

28. Q: what are the problems with the following procedures? How to fix it?

Answer: the statement printcollection (listinteger); An error is reported during compilation, because the generic parameters have no inheritance relationship. The repair method is to use? Wildcard, printcollection (collection collection), because methods related to parameter types, such as collection, cannot appear in the method printcollection (collection collection) Add(), because the parameters passed in when the program calls this method do not know what type, but methods independent of parameter types can be called, such as

29. Q: please explain the implementation and reasons of the following program segments?

A: t0 compilation directly reports an error. One of the two parameters of add is integer and the other is float, so the minimum level of the same parent class is number, so t is of type number and t0 is of type int, so the type is wrong.

T0, T1, T2 and T3 actually demonstrate several cases where a generic method is called without specifying a generic type. T4, T5 and T6 demonstrate the cases where a generic method is called to specify a generic type. When calling generic methods, you can specify generics or not; When you do not specify a generic type, the type of the generic variable is the smallest level of the same parent class (up to object) of several types in the method. When you specify a generic type, several types in the method must be the generic instance type or its subclass. Remember, the java compiler first checks the type of the generic in the code, then erases the type, and then compiles.

30. Q: what is the difference between the following two methods? Why?

A: the GET1 method directly compiles incorrectly. Because the compiler compiles only after the generic check and generic erasure are performed before compiling, t can only call the method of object because there is no type limit to automatically erase it as the object type when it is actually compiled, and object has no CompareTo method.

The get2 method adds a generic type qualification, which can be used normally. Because the qualified type is a comparable interface and there is a CompareTo method, T1 and T2 are forced to succeed after being erased. Therefore, type qualification can be used in generic classes, generic interfaces and generic methods. However, no matter whether the qualification is a class or an interface, the extensions and & symbols are used. If the qualified type has both interfaces and classes, there must be only one class and put it in the first place. If the generic type variable has multiple qualifications, the original type is replaced with the type variable of the first boundary.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>