Understanding of generics in Java and generic related interview questions

1. Why do you need generics?
First look at the following code:

List list = new ArrayList();
list.add("CSDN_SEU_Calvin");
list.add(100);
for (int i = 0; i < list.size(); i++) {
  String name = (String) list.get(i); //取出Integer时,运行时出现异常
  System.out.println("name:" + name);
}


This example adds a string type value and an Integer type value to the list type collection (this is legal because the default type of the list at this time is the Object type).
In the loop, due to forgetting to add an Integer value or other reasons, java.lang.ClassCastException will occur at runtime. To solve this problem, generics came into being.

2. The use of
generics Java generic programming was introduced after JDK1.5. Generics allow programmers to use type abstraction, usually used in collections .
As long as the first line of code in the above example is changed to the following form, an error will be reported when compiling list.add(100).
List<String> list = new ArrayList<String>();
Through List<String>, it is directly restricted that the list collection can only contain elements of type String, so that in the 6th line in the above example, there is no need to perform type conversion , Because the collection can remember the type information of its elements, the compiler has been able to confirm that it is of type String.

3. Generics are only valid at the compilation stage.
Look at the following code:
ArrayList<String> a = new ArrayList<String>();
ArrayList b = new ArrayList();
Class c1 = a.getClass();
Class c2 = b.getClass( );
System.out.println(c1 == c2); //true
The output result of the above program is true. Because all reflection operations are at runtime, since it is true, it proves that after compilation, the program will take de-generic measures.

In other words, generics in Java are only valid during the compilation phase. In the compilation process, after correctly checking the generic result, the related information of the generic will be erased, and the method of type checking and type conversion will be added at the boundary of the object entering and leaving the method. In other words, the successfully compiled class file does not contain any generic information.

The above conclusion can be confirmed by the following reflection example:

ArrayList<String> a = new ArrayList<String>();
a.add("CSDN_SEU_Calvin");
Class c = a.getClass();
try{
     Method method = c.getMethod("add",Object.class);
     method.invoke(a,100);

}catch(Exception e){
e.printStackTrace();
}System.out.println(a);


Because it bypasses the compilation phase, generics are bypassed, and the output is:
[CSDN_SEU_Calvin, 100] 4. Generic classes and generic methods
are as follows. Let’s look at an example of the use of generic classes and methods, and compare them with the use of non-generic methods. The output results of the two are the same. Post them here for readers to understand the differences. . If you are interested in generic interface examples, you can find some information, so I won’t go into details here.
(1) The case of using generics

public static class FX<T> {
private T ob; // 定义泛型成员变量
public FX(T ob) {
this.ob = ob;
}
public T getOb() {
return ob;
}

public void showType() {
System.out.println("T的实际类型是: " + ob.getClass().getName());
}
}
public static void main(String[] args) {
FX<Integer> intOb = new FX<Integer>(100);
intOb.showType();
System.out.println("value= " + intOb.getOb());
System.out.println("----------------------------------");


FX<String> strOb = new FX<String>("CSDN_SEU_Calvin");
strOb.showType();
System.out.println("value= " + strOb.getOb());
}

(2) When not using generics

public static class FX {
private Object ob; // 定义泛型成员变量

public FX(Object ob) {
this.ob = ob;
}
public Object getOb() {
return ob;
}

public void showType() {
System.out.println("T的实际类型是: " + ob.getClass().getName());
}
}
public static void main(String[] args) {
FX intOb = new FX(new Integer(100));
intOb.showType();
System.out.println("value= " + intOb.getOb());
System.out.println("----------------------------------");

FX strOb = new FX("CSDN_SEU_Calvin");
strOb.showType();
System.out.println("value= " + strOb.getOb());
}


The output results of both writing methods are:
The actual type of T is: java.lang.Integer
value = 100
---------------------------- ------
The actual type of T is: java.lang.String
value = CSDN_SEU_Calvin

5. Wildcard
In order to introduce the concept of wildcard, first look at the following code:

List<Integer> ex_int= new ArrayList<Integer>();  
List<Number> ex_num = ex_int; //非法的


There will be a compilation error in line 2 above, because although Integer is a subclass of Number, List<Integer> is not a subclass of List<Number> .

Assuming there is no problem with the second line of code, then we can use the statement ex_num.add(newDouble()) to load various types of subclasses in a List. This is obviously not possible, because we are taking out the list of When it comes to objects, it is not clear whether to transform to Integer or Double. Therefore, we need a reference type that can be logically expressed as the parent class of List<Integer> and List<Number> at the same time, and type wildcards have emerged. In this case, it can be expressed as List<?>. The following example also illustrates this point, the comment has been written very clearly.
 

public static void main(String[] args) {
FX<Number> ex_num = new FX<Number>(100);
FX<Integer> ex_int = new FX<Integer>(200);
getData(ex_num);
getData(ex_int);//编译错误
}
public static void getData(FX<Number> temp) { //此行若把Number换为“?”编译通过
//do something...
}

public static class FX<T> {
private T ob;
public FX(T ob) {
this.ob = ob;
}
}

6. Upper and lower boundary You
can understand after reading the upper boundary example below, the form of the lower boundary FX<? supers Number> will not be repeated.

public static void main(String[] args) {
FX<Number> ex_num = new FX<Number>(100);
FX<Integer> ex_int = new FX<Integer>(200);
getUpperNumberData(ex_num);
getUpperNumberData(ex_int);
}

public static void getUpperNumberData(FX<? extends Number> temp){
      System.out.println("class type :" + temp.getClass());
}

public static class FX<T> {
private T ob;
public FX(T ob) {
this.ob = ob;
}
}

7. The benefits of generics
(1) Type safety .
By knowing the variable type restrictions defined by generics, the compiler can more effectively improve the type safety of Java programs.
(2) Eliminate forced type conversion .
Eliminate many casts in the source code. This makes the code more readable and reduces the chance of errors. All casts are automatic and implicit.
(3) Improve performance .
Lits list1 = new ArrayList();
list1.add("CSDN_SEU_Calvin ");
String str1 = (String)list1.get(0);

List<String> list2 = new ArrayList<String>();
list2.add("CSDN_SEU_Calvin ");
String str2 = list2.get(0);
For the above two programs, all the work due to generics is in the compiler Finished, the bytecode compiled by javac is the same (just to ensure type safety), so what about performance improvement? It is because in the implementation of generics, the compiler inserts forced type conversion into the generated bytecode, but the fact that more type information can be used by the compiler makes it possible to optimize future versions of the JVM.

8. Precautions for the use of generics
(1) The type parameters of generics can only be class types (including custom classes), not simple types.
(2) There can be multiple generic type parameters.
(3) The instanceof operation cannot be used on the exact generic type. If the following operations are illegal, errors will occur during compilation.

if(ex_num instanceof FX<Number>){    
}
(4) Cannot create an array of exact generic type . The following uses an example from a Sun document to illustrate this problem:
List<String>[] lsa = new List<String>[10]; // Not really allowed.  
Object o = lsa;  
Object[] oa = (Object []) o;  
List<Integer> li = new ArrayList<Integer>();  
li.add(new Integer(3));  
oa[1] = li; // Unsound, but passes run time store check  
String s = lsa[1].get(0); // Run-time error: ClassCastException.  

In this case, due to the JVM generic erasure mechanism, the JVM does not know the generic information at runtime, so an ArrayList<Integer> can be assigned to oa[1] without exception, but the data is retrieved When you have to do a type conversion, so there will be ClassCastException, if the declaration of a generic array can be made, the above mentioned situation will not appear any warnings and errors at compile time, and errors will only occur at runtime .

The following wildcards are allowed:
List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.  
Object o = lsa;  
Object[] oa = (Object[ ]) o;  
List<Integer> li = new ArrayList<Integer>();  
li.add(new Integer(3));  
oa[1] = li; // Correct.  
Integer i = (Integer) lsa[1] .get(0); // OK

9. List and List<?>
(1) List is actually List<Object>. List actually represents a native List holding any Object type.
(2) And List<?> represents a non-native List with a certain type, but we don't know what that type is.

Generic related interview questions:

1. What are generics in Java? What are the benefits of using generics?
Generics are a mechanism for parameterizing types. It can make the code applicable to various types, so as to write more general code, such as collection framework.

Generics are a compile-time type confirmation mechanism. It provides type safety at compile time, ensuring that only objects of the correct type can be used on generic types (usually generic collections), and avoiding ClassCastException at runtime.

2. How does Java's generics work? What is type erasure?
The normal work of generics is to rely on the compiler to perform type checking when compiling the source code, then type erasure and insert where the type parameters appear The relevant instructions of forced conversion are realized.

The compiler erases all type-related information at compile time, so there is no type-related information at runtime. For example, List<String> is represented by only one List type at runtime. Why do you want to erase it? This is to avoid type expansion .

3. What are the qualified wildcards and non-qualified wildcards in generics? The
qualified wildcards restrict the type. There are two qualified wildcards, one is <? extends T>, which sets the upper bound of the type by ensuring that the type must be a subclass of T, and the other is <? super T>, which ensures that the type must be the parent of T Class to set the lower bound of the type. The generic type must be initialized with the type within the limit, otherwise it will cause a compilation error. On the other hand, <?> represents an unqualified wildcard, because <?> can be replaced by any type.

4. What is the difference between List<? extends T> and List <? super T>?
This is related to the previous interview question. Sometimes interviewers will use this question to evaluate your understanding of generics instead of asking directly What are you qualified wildcards and non-qualified wildcards. These two List declarations are examples of qualified wildcards. List<? extends T> can accept any List inherited from T, and List<? super T> can accept any List formed by the parent class of T. For example, List<? extends Number> can accept List<Integer> or List<Float>. More information can be found in the links that appear in this paragraph.

5. How to write a generic method so that it can accept generic parameters and return generic types? It
is not difficult to write generic methods. You need to use generic types instead of primitive types, such as using T, E or K, V And other widely recognized type placeholders. For examples of generic methods, please refer to the Java Collection 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 to use generics to write classes with parameters in Java?
This is an extension of the previous interview question. The interviewer may ask you to write a type-safe class using generics instead of writing a generic method. The key is still to use generic types instead of primitive types, and to use the standard placeholders used in the JDK.

7. Write a generic program to implement LRU caching?
This is equivalent to an exercise for those who like Java programming. To give you a hint, LinkedHashMap can be used to implement a fixed-size LRU cache. When the LRU cache is full, it will remove the oldest key-value pair from the cache. LinkedHashMap provides a method called removeEldestEntry(), which will be called by put() and putAll() to delete the oldest key-value pair.

package com.java.oop.features;
import java.util.LinkedHashMap;
//构建基于LRU算法的缓存对象(简易)
//缓存满了要淘汰长时间不访问的对象
class LruCache<K,V> extends LinkedHashMap<K,V>{
	  LinkedHashMap<K,V> removeElements=
			new LinkedHashMap<K, V>();//放移除的对象
	  private int maxCap;//记录最大容量
	  public LruCache(int cap) {
		super((int)Math.ceil(cap/0.75f)+1,0.75f,true);//调用父类有参构造
	    this.maxCap=cap;
	  }
	  //当我们执行put方法时,每次都会调用此方法
	  //方法返回值为true时表示满了,此时可以移除数据
	  @Override
	  protected boolean removeEldestEntry(
		java.util.Map.Entry<K, V> eldest) {
		boolean flag= size()>maxCap;
		if(flag) {
			removeElements.put(eldest.getKey(), eldest.getValue());
		}
		return flag;
	  }
}
public class TestExtends02 {
   public static void main(String[] args) {
	  LruCache<String,Object> cache=
	  new LruCache<>(3);
      cache.put("A", 100);
      cache.put("B", 200);
      cache.put("C", 300);
      cache.get("A");
      cache.put("D", 400);
      cache.put("E", 500);
      System.out.println(cache);
      System.out.println(cache.removeElements);
   } 
}

Output result:

{A=100, D=400, E=500}
{B=200, C=300}

8. Can you pass List<String> to a method that accepts List<Object> parameters?
For anyone who is not familiar with generics, this Java generics topic looks confusing, because at first glance String is a kind of Object, so List<String> should be used where List<Object> is needed ,But that is not the case. Doing so will cause compilation errors. If you think about it further, you will find that Java makes sense to do this, because List<Object> can store any type of object including String, Integer, etc., while List<String> can only be used to store Strings.
 

List<Object> objectList;  
List<String> stringList;   
objectList = stringList;  //compilation error incompatible types  

9. Can generics be used in Array?
This is probably the simplest one of the Java generics interview questions, of course, the premise is that you have to know that Array does not support generics in fact , which is why Joshua Bloch in Effective Java book It is recommended to use List instead of Array, because List can provide compile-time type safety guarantees, but Array cannot.

10. How to prevent unchecked warnings in Java?
If you mix generic and primitive types, such as the following code, Java 5's javac compiler will generate unchecked warnings, such as List<String> rawList = new ArrayList()
Note: Hello.java uses unchecked or unsafe operations;
such warnings can be shielded by the @SuppressWarnings("unchecked") annotation.

11. What is the difference between List<Object> and primitive type List in Java?
The main difference between primitive type and parameter type <Object> is that the compiler will not perform type safety checks on primitive types at compile time, but will Check the type with parameters . By using Object as the type, you can tell the compiler that the method can accept any type of object, such as String or Integer. The focus of this question lies in the correct understanding of primitive types in generics. The second 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 it will produce Compile Error.
12. What is the difference between List<?> and List<Object> in Java? This
question looks very similar to the previous question, but is completely different in essence. List<?> is a List of unknown type, and List<Object> is actually a List of any type. You can assign List<String>, List<Integer> to List<?>, but you cannot assign List<String> to List<Object>.   

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 List<String> and primitive type List.
This question is similar to "What is the difference between primitive type and parameter type". The parameter type is type-safe, and its type safety is guaranteed by the compiler , but the primitive type List is not type-safe. You cannot store any type of Object other than String in the String type List, but you can store any type of object in the original List. You don't need to use a generic type with parameters for type conversion, but for primitive types, you need to perform an explicit type conversion.

 

List listOfRawTypes = new ArrayList();  
listOfRawTypes.add("abc");  
listOfRawTypes.add(123); //编译器允许这样 - 运行时却会出现异常  
String item = (String) listOfRawTypes.get(0); //需要显式的类型转换  
item = (String) listOfRawTypes.get(1); //抛ClassCastException,因为Integer不能被转换为String  
        
List<String> listOfString = new ArrayList();  
listOfString.add("abcd");  
listOfString.add(1234); //编译错误,比在运行时抛异常要好  
item = listOfString.get(0); //不需要显式的类型转换 - 编译器自动转换  

 

This blog post was compiled by Xiaobai on the shoulders of the Great God. I sincerely thank the great gods for their selfless dedication and generous spirit. This quality has brought us a lot of help to the growth of Xiaobai in technology.
Reference:
https://blog.csdn.net/seu_calvin/article/details/52230032
https://blog.csdn.net/sunxianghuang/article/details/51982979
—————————————— -
Disclaimer: this article is CSDN blogger original article "ten beans three Exhibition", and follow CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
Original link: https://blog.csdn.net/zz13995900221/article/details/79736057

Guess you like

Origin blog.csdn.net/qianzhitu/article/details/102995570