Java Basic Review --- Generics (size)

Generics:
Generics are an important part of the java foundation, and it is necessary to master generics. Next, I will explain with my understanding

背景:编译器是先通过检查代码中泛型的类型 再进行类型擦除 再进行编译的
  1. what is generics,
  2. When do you need generics,
  3. How to use generics,
  4. Generics note.

At present, the generic application I have come into contact with is a collection using generics to achieve only one type of data in a collection. The benefits of
generics :
1. The runtime exceptions are advanced to compile time.
2. Avoid unnecessary casts.

Common applications of generics in collections:

ArrayList<String>  list = new ArrayList<String>();  true     推荐使用。

ArrayList<Object>  list = new ArrayList<String>();  false
ArrayList<String>  list = new ArrayList<Object>();  false

//以下两种写法主要是为了兼顾新老系统的兼用性问题。
 * 
ArrayList<String>  list = new ArrayList();           true   

ArrayList    list = new ArrayList<String>();   true   

Note: Generics do not have the concept of polymorphism. The data types on the left and right sides must be consistent, or just write the generic type on one side.

Recommended use: Write generics on both sides.
The specific collection is of the above type, what is the generic type on the left?

A common example:

gather:

ArrayList  list = new ArrayList(); 
    list.add("11");
    list.add(3);

At this time, there are multiple types stored in the ArrayList collection. Does it look unpleasant? In daily applications, the result we want is that the types stored in a collection are the same.
Such as:

ArrayList<String>  list = new ArrayList<String>(); 
    list.add("11");
    list.add(3);
    //这个时候 集合list只能存放String类型的数据 
    //如果存储了其他的如 Integer类型之类的就会报错,是在编译前检查

generic method

 public <T> T showKeyName(T a){//<T>是声明泛型方法
    return a;
}

Generic class:
custom generic: A custom generic is a placeholder for a data type or a variable for a data type.

class Point< T>{ // 此处可以随便写标识符号,T是type的简称 一般都是T  
 private T var ; // var的类型由T指定,即:由外部指定  
 public T getVar(){ // 返回值的类型由外部决定  
  return var ;  
 }  
 public void setVar(T var){ // 设置的类型也由外部决定  
  this.var = var ;  
 }  
};
注意:在方法上自定义泛型,这个自定义泛型的具体数据类型
是在调用该方法的时候传入实参时确定具体的数据类型的。 

泛型类还可以同时定义多个泛型 栗子:
class Point< K,V>{ // 不一样的泛型  
 private K var ;   // var的类型由K指定
 private V time ;  //time的类型由V指定
 }   
};   

泛型类中要注意一:   静态方法无法访问类上定义的泛型
如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法
(原因我也不知道)
声明泛型一定要写在static后,返回值类型之前

class MyMessage<T>{
    public T qu(T a) {
        System.out.println("qu");
        return a;
    }
    public static <t> t ha(t a){    //静态函数需要重新泛型
        System.out.println("ha");
        return a;
    }
}

class new1{
    public static void main(String[] args) {        
        System.out.println(MyMessage.ha("aaa"));
    }
}
注意 如果是调用静态的泛型函数时不能MyMessage<String>.ha("aaa")
传入具体泛型

泛型类中要注意二:
泛型类与泛型方法共存:
public class Test1<T>{

    public  void testMethod(T t){
        System.out.println(t.getClass().getName());
    }
    public  <T> T testMethod1(T t){
        return t;
    }
}
上面代码中,Test1<T> 是泛型类,testMethod 是泛型类中的普通方法,
而testMethod1是一个泛型方法。而泛型类中的类型参数与泛型方法中的类型参数
是没有相应的联系的,泛型方法始终以自己定义的类型参数为准。
意思就是调用testMethod1 结果是里面里面的那个决定的 而不是外面的那个类
如果在一个泛型类中存在泛型方法,那么两者的类型参数最好不要同名。

Generic methods and varargs

再看一个泛型方法和可变参数的例子:

public <T> void printMsg( T... args){
for(T t : args){
    System.out.println(t);
}

}


Generic parent class subclass inheritance:

class Son1 extends MyMessage<String>{  
 //指的是父类里面T是用String来代替了  
 //如果有重写的话 那么重写的父类类型是String
    public String ha(String a) {
        return a;
    }
}
class Son2<T> extends MyMessage<T>{
    public T xx(T a) {
        return a;
    }
}

Generic interface:

class person<T>{
public  T a(T az) {
    System.out.println("futher");
    return az;
    }
}
class son extends person<Integer>{

@Override
public Integer a(Integer az) {
    System.out.println("son");
    return az;
}
}

Note: The instanceof operation cannot be used on exact generic types. If the following operations are illegal, an error will occur when compiling.

 List<String> aList=new ArrayList<String>(); 
 aList instanceof List<String>//编译错误


Generics has a term called generic erasure , which means that although there are different generics, the computer only sees the Object type after compilation. For example:

 List<String> aList1=new ArrayList<String>(); 
 List<Integer> aList2=new ArrayList<Integer>(); 
  编译后就变成
List aList1=new ArrayList(); 
List aList2=new ArrayList();  
    //在编译生成的字节码中不包含泛型中的类型参数,类型参数会在编译时去掉。
    //例如:List<String> 和 List<Integer> 在编译后都变成 List。

Generic wildcards
Let's look at a problem first:

  public void showKeyValue1(Generic<Number> obj){
  Log.d("泛型测试","key value is " + obj.getKey());
}
Generic<Integer> gInteger = new Generic<Integer>();
Generic<Number> gNumber = new Generic<Number>();
showKeyValue(gInteger);

// showKeyValue这个方法编译器会为我们报错:

Let's solve why the error is reported

问:  首先有个疑惑 不是有泛型擦除吗?为什么Generic<Integer>传递给  
    Generic<Number>的参数会报错 编译后不是变成Generic类型传递给Generic吗?

Answer: The type check is judged before compilation, so the type is judged first and then the generic type is erased
. Let's look at a question:

  public void showKeyValue1(Generic<Number> obj){
  Log.d("泛型测试","key value is " + obj.getKey());
}
Generic<Integer> gInteger = new Generic<Integer>();
Generic<Number> gNumber = new Generic<Number>();
showKeyValue(gInteger);

// showKeyValue这个方法编译器会为我们报错:

Let's solve why the error is reported

问:  首先有个疑惑 不是有泛型擦除吗?为什么Generic<Integer>传递给  
    Generic<Number>的参数会报错 编译后不是变成Generic类型传递给Generic吗?

Answer: The type check is judged before compilation, so the type is judged first and then the generic type is erased

问:Integer不是继承Number吗 为什么还无法传递?

Answer: The inheritance relationship of generic classes of the same parameter type depends on the inheritance structure of the generic class itself.

List<Integer>和List<Number>是不同的对象 没有继承关系

The premise is that the generic class itself must have an inheritance relationship , and the generic inheritance is useless .

例如 List<String> 是 Collection<String> 的子类
    List<Integer> 不是 Collection<Number> 的子类
栗子:
传入的参数是List<Integer>
形参1.public static String print(List<Integer> a) 可以通过
形参2.public static String print(List<Number> a)  
不能通过  两个维度没有继承关系
形参3.public static<T> String print(List<T> a)    可以通过
形参4.public static String print(List<?> a)       可以通过
public static void zz(Collection<Integer> a)      可以通过  因为存在继承
public static void zz(Collection<Number> a)       不可以
传入参数List<String>
形参1 public static void xx(List<?> a)可以
形参2 public static void xx(List<? extends Number> a) 不可以 因为限制了范围
当类型声明中使用通配符 ? 时,
其子类型可以在两个维度上扩展。
例如 Collection<Number>
在维度1上扩展:List<? extends Number>
在维度2上扩展:Collection<Integer>
两个维度上同时扩展:List<Integer>
意思就是  Collection<Number>可以接收List<Integer>

Summarize:

引入泛型之后的类型系统增加了两个维度:一个是类型参数自身的继承体系结构,
另外一个是泛型类或接口自身的继承体系结构。第一个指的是对于 List<String>List<Object>这样的情况,类型参数String是继承自Object的。
而第二种指的是 List接口继承自Collection接口。
对于这个类型系统,有如下的一些规则:

相同类型参数的泛型类的关系取决于泛型类自身的继承体系结构。List<String>是Collection<String> 的子类型,
List<String>可以替换Collection<String>。
这种情况也适用于带有上下界的类型声明。 
当泛型类的类型声明中使用了通配符的时候,其子类型可以在两个维度上分别展开
如对Collection<? extends Number>来说,
其子类型可以在Collection这个维度上展开,List<? extends Number>和Set<? extends Number>等;
也可以在Number这个层次上展开,即Collection<Double>和
 Collection<Integer>等。如此循环下去,ArrayList<Long>和
 如果泛型类中包含多个类型参数,则对于每个类型参数分别应用上面的规则。

Wildcard writing:

public static void zz(List<?> a)  
通配符是实参而且还是根实参 不是形参 所以可接受任何泛型对象
还可以定义上边界和下边界:

Upper boundary:

public static void zz(List<? extends Number> a){ 
// 只能接收Number及其Number的子类  }

Lower boundary:

public static void zz(List<? super Integer > a){ 
// 只能接收Integer及其Integer的父类  }

If the type variable is bounded then the original type is replaced by the type variable of the first bound

The elements that cannot be added to the collection after the upper bound wildcard are also restricted by the parent element add of the upper bound. The elements that can be added to the
next wildcard must be subclasses or the type of the elements taken out by themselves is restricted by Object get. To
obtain data, use the extend wildcard to add data. super wildcard Do not use wildcards if you want
both
, only super has permission to add others, you can only view them.
Mention :


Java generics cannot upcast

class Info< T>{  
 private T var ;  // 定义泛型变量  
 public void setVar(T var){  
  this.var = var ;  
 }  
 public T getVar(){  
  return this.var ;  
 }  
 public String toString(){ // 直接打印  
  return this.var.toString() ;  
 }  
};  
public class GenericsDemo23{  
 public static void main(String args[]){  
  Info< String> i1 = new Info< String>() ;  // 泛型类型为String  
  Info< Object> i2 = null ;  
  i2 = i1 ;        //这句会出错 incompatible types  因为两者不是一个对象
 }  
};  

and:

Generic type parameters cannot be used in catch statements in Java exception handling. Because exception handling is performed by the JVM at runtime. Because the type information is erased, the JVM is unable to distinguish between the two exception types MyException; and MyException. To the JVM, they are all of
type MyException. Also, the catch statement corresponding to the exception cannot be executed.

Important: You cannot use basic data types in generics. If you need to use basic data types, use the wrapper types corresponding to the basic data types.


Bridge method:

看看下面这个类SonPair
class SonPair extends Pair<String>{  
      public void setFirst(String fir){....}  
}

Obviously, the programmer's intention is to override the setFirst(T fir) method of the parent class Pair in the SonPair class. But in fact, the setFirst(String fir) method in SonPair does not cover this method in Pair at all.
The reason is very simple, Pair has been type erased to Pair during the compilation phase, and its setFirst method has become setFirst(Object fir). So of course setFirst(String) in SonPair cannot override setFirst(Object) of the parent class.
This is indeed a lot of trouble for polymorphism, let's see how the compiler solves this problem.
The compiler will automatically generate a bridge method in SonPair:

public void setFirst(Object fir)
{
    setFirst((String) fir)
} 

In this way, SonPair's bridge method can indeed override setFirst(Object) of the generic parent class. And the bridge method actually calls the subclass byte setFirst(String) method. For polymorphism it's fine.
1.2) The problem is not over, the method coverage in polymorphism is OK, but the bridge method brings a question:

Now, what if we also wanted to override the getFirst() method in SonPair?

class SonPair extends Pair<String>
{
      public String getFirst(){....}  
}  

Since a bridge method is required to override getFirst in the parent class, the compiler will automatically generate a public Object getFirst() bridge method in SonPair. (Dry goods - the bridge method is introduced, which is generated by the compiler, not by the programmer)
However, the question is coming, there are two methods with the same method signature in SonPair (only the return type is different):
①String getFirst () // Self-defined method
②Object getFirst() // Bridge method generated by the compiler
Does the compiler allow multiple methods with the same method signature to exist in a class? In fact, there is a knowledge point that everyone may not know:
① The method signature really only has the method name + parameter list. There is no doubt about that!
② We must not write multiple methods with the same method signature. If the program is written like this, the compiler will not let it go. There is no doubt about that!
③ The most important point is: JVM will use the parameter type and return type to determine a method. Once the compiler somehow compiles two methods with the same method signature (only the compiler can create this miracle, we programmers can't write this kind of code artificially). The JVM can still distinguish these methods, provided that the return types are different.

in conclusion:

At compile time, all type parameters of generic classes are replaced by Object or their bounds. (Type erasure)
When inheriting generic types, the synthesis of bridge methods is to avoid the polymorphism disaster caused by erasure of type variables.

Finally, a mention of generic arrays:

List<String>[] ls = new ArrayList<String>[10];  
而使用通配符创建泛型数组是可以的,如下面这个例子:
List<?>[] ls = new ArrayList<?>[10]; 

Generic arrays are not supported in java because of generic erasure. If a generic array
Object[] aa=new ArrayList[]; is created,
aa[0]=new ArrayList(); The compiler is cheated then it's not safe

And covariance... don't really understand

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326006373&siteId=291194637
Recommended