java泛型详解

java泛型详解

主要参考网址:https://zhuanlan.zhihu.com/p/331620060


泛型和Object的区别

1.泛型不需要做强制类型转换

2.编译时更安全

如果使用Object类的话,可能转换异常 — ClassCastException

案例

 Foo newFoo = (Foo) my.doSomething(foo); // 可能转换异常
 Foo newFoo = my.doSomething(foo);   // 编译时更安全

 

泛型分类

泛型类、泛型接口、泛型方法

泛型常用标记含义

E - Element (在集合中使用,因为集合中存放的是元素)

T - Type(Java 类)

K - Key(键)

V - Value(值)

N - Number(数值类型)

泛型类

 //在实例化泛型类时,必须指定具体类型
 public class Generic<T>{ 
     //key这个成员变量的类型为T, T的类型由外部指定  
     private T key;
 ​
     public Generic(T key) {
         this.key = key;
     }
 ​
     public T getKey(){
         return key;
     }
 }
 ​
 //泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
 Generic<Integer> genericInteger = new Generic<Integer>(10000);
 Generic<String> genericString = new Generic<String>("minnersun");

 

泛型接口

定义泛型接口

 //定义一个泛型接口
 public interface Generator<T> {
     public T next();
 }

实现类1,未传实参

未传入泛型实参时,与泛型类的定义相同

在声明类的时候,需将泛型的声明也一起加到类中

 // 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
 class FruitGenerator<T> implements Generator<T>{
     @Override
     public T next() {
         return null;
     }
 }

实现类2,传实参

在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型

 // 即:Generator<T>,public T next();中的的T都要替换成传入的String类型
 public class FruitGenerator implements Generator<String> {
 ​
     private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
 ​
     @Override
     public String next() {
         Random rand = new Random();
         return fruits[rand.nextInt(3)];
     }
 }

  

泛型方法

基本用法

 public class GenericTest {
    public class Generic<T>{     
         private T key;
 ​
         public Generic(T key) {
             this.key = key;
         }
 ​
         // 注意!!! 虽然在方法中使用了泛型,但是这并不是一个泛型方法。
         // 这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
         public T getKey(){
             return key;
         }
     }
 ​
     /** 
      * 这才是一个真正的泛型方法
      * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
      * 这个T可以出现在这个泛型方法的任意位置
      * 泛型的数量也可以为任意多个 
      *    如:public <T,K> K showKeyName(Generic<T> container){
      *        ...
      *        }
      */
     public <T> T showKeyName(Generic<T> container){
         System.out.println("container key :" + container.getKey());
         //当然这个例子举的不太合适,只是为了说明泛型方法的特性。
         T test = container.getKey();
         return test;
     }
 ​
     //这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
     public void showKeyValue1(Generic<Number> obj){
         Log.d("泛型测试","key value is " + obj.getKey());
     }
 ​
     //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
     //同时这也印证了泛型通配符所描述的,?是一种类型实参,可以看做为Number等所有类的父类
     public void showKeyValue2(Generic<?> obj){
         Log.d("泛型测试","key value is " + obj.getKey());
     }
 ​
      /**
      * 注意!!!
      * 只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
      * 译器会为我们提示错误信息:"UnKnown class 'E' "
      */
     public <T> T showKeyName(Generic<E> container){
         ...
     }  
 ​
 }

可变参数

     public static  <T> void printMsg(String str,T... args){
         for (T t: args){
             System.out.println(t);
         }
     }
     printMsg("minnersun",20000,"summer",44.45); // 20000 summer 44.45

静态方法与泛型

因为静态方法加载顺序与类同级,所以 静态方法无法访问类上定义的泛型

必须要将泛型定义在方法上

 public class StaticGenerator<T> {
     public static <T> void show(T t){
 ​
     }
 }

 

 

泛型上下边界

为泛型添加上边界

传入的类型实参必须是指定类型的子类型

 public void showKeyValue1(Generic<? extends Number> obj){
     System.out.println("泛型测试","key value is " + obj.getKey());
 }
 ​
 Generic<String> generic1 = new Generic<String>("10000");
 Generic<Integer> generic2 = new Generic<Integer>(10000);
 Generic<Float> generic3 = new Generic<Float>(5.5f);
 Generic<Double> generic4 = new Generic<Double>(3.15);
 ​
 //这一行代码编译器会提示错误,因为String类型并不是Number类型的子类
 //showKeyValue1(generic1);
 ​
 showKeyValue1(generic2);
 showKeyValue1(generic3);
 showKeyValue1(generic4);
 public class Generic<T extends Number>{
     private T key;
 ​
     public Generic(T key) {
         this.key = key;
     }
 ​
     public T getKey(){
         return key;
     }
 }
 ​
 //这一行代码也会报错,因为String不是Number的子类
 Generic<String> generic1 = new Generic<String>("10000");

下边界

必须在权限声明与返回值之间<T>上添加上下边界,即在泛型声明的时候添加

//public <T> T showKeyName(Generic<T extends Number> container),编译器会报错:"Unexpected bound"
public <T extends Number> T showKeyName(Generic<T> container){
    System.out.println("container key :" + container.getKey());
    T test = container.getKey();
    return test;
}

  



 
posted @ 2021-08-05 14:32  minnersun  阅读(252)  评论(0编辑  收藏  举报