java 泛型知识总结

首先说一下什么是泛型

泛型,就是参数化类型。我们都知道在定义方法的时候,可以传入形参,在使用的时候实参,这是我们经常使用的,但是什么是参数化类型呢?顾名思义,就是将类型进行参数化,一般我们经常见到的分为三种:泛型类(class)、泛型接口(interface)、泛型方法(method)。

我们为什么使用泛型

可以通过如下代码说明:

  1. 将错误发现在编译器
        List list = new ArrayList();
        list.add("first");
        list.add("second");
        list.add(3);

        for (int i = 0; i < list.size(); i++) {
            System.out.println("name:"+(String) list.get(i));
        }

以上代码在编译器不会出现问题,但是在运行的时候会抛出异常ClassCastException,是因为在向list集合放入数据的时候,list不会记录放入数据的类型,但是在取出的时候会恢复之前的类型,如果不小心将非String类型放入集合,在运行时就会出现问题。

  1. 相同的代码运行不同的数据类型,代码简化

        public float add (float a, float b){
            return a+b;
        }
    
        public double add(double a, double b){
            return a+b;
        }
    

    看到上面的代码就不多做解释了,如果还有需要int类型的加法,是不是还要新增加一个方法呢

泛型的定义方式
  1. 泛型类,示例如下:

    public class Generic<T,K> {
        public T mData;
        public K mResult;
    
        public Generic(T data,K result){
            mData = data;
            mResult = result;
        }
    
        public void print(){
            System.out.println("data:"+mData);
            System.out.println("result:"+mResult);
        }
    }
    

    引用类型变量T、K,在类名后面,用<>包裹,个数不限,类型变形一般使用大写字母,如T、E、K、V等

  1. 泛型接口,与泛型类相似:
public interface GenericInterface<T> {
    T next();
}
  1. 泛型方法:

      public <T, K> T test_2(Generic<T, K> generic) {
            return generic.mData;
        }
    

    在public与返回值之间定义泛型类型,并用<>包裹这是必不可少的,并且T、K可以出现在泛型方法的任何位置。以下方法并不是泛型方法,仅仅是返回值为泛型而已:

    //不是泛型方法  
    public T next() {
            return data;
        }
    
   
   
泛型类的约束和局限性
public class Restrict<T> {
}
  1. 不能使用基本类型
//不能使用基本类型
        Restrict<double> restrict = new Restrict<>();
        Restrict<Double> restrict1 = new Restrict<>();
  1. 运行时的类型检查仅支持原始类型
  ```java
  //运行时类型查询仅支持原始类型
          Restrict<String> restrict2 = new Restrict<>();
          //如下两种是错误的判断方式
          System.out.println(restrict2 instanceof Restrict<Double>);
          System.out.println(restrict2 instanceof Restrict<T>);
  //正确的方式
  Restrict<Integer> restrict3 = new Restrict<>();
          System.out.println(restrict2.getClass() == restrict3.getClass());
  ```
  1. 静态方法或者静态代码块中不能引用泛型类型变量
     ```java
       //泛型类的静态上下文失效
          public K data;
         static {
             System.out.println(data);
         }
     
         public static void testF(){
             System.out.println(data);
         }
     ```
   
     不能在静态域或方法中引用类型变量。因为泛型是要在对象创建的时候才知道是什么类型的,而对象创建的代码执行先后顺序是static的部分,然后才是构造函数等等。所以在对象初始化之前static的部分已经执行了,如果你在静态部分引用的泛型,那么毫无疑问虚拟机根本不知道是什么东西,因为这个时候类还没有初始化。
  1. 不能实例化类型变量
        ```java
            T data;
            public Restrict(){
        this.data = new T();
            }
        ```
泛型类的继承关系
public class Employee {
}

public class Worker extends Employee {
}

public class Pair<T> {
}

以下创建对象方式是不允许的:

Pair<Employee> pair = new Pair<Worker>();

但是可以继承或者或者泛型类,如ArrayList和List:

public class ExtentPair extends Pair {
}

Pair pair1 = new ExtentPair();
通配符类型

通过上面的例子,Pair和Pair没有继承关系,就会出现如下错误:

public  class Fruit {
    public  String getColor(){
        return "fruit";
    };
}

public class Orange extends Fruit {
    @Override
    public String getColor() {
        return "Orange";
    }
}

public class Apple extends Fruit {

    @Override
    public String getColor() {
        return "Apple";
    }
}

public class HongFuShi extends Apple {

    @Override
    public String getColor() {
        return "HongFuShi";
    }
}

public class GenericType<T> {
    T data;

    public T getData() {
        return data;
    }

    public void setData(T data){
        this.data = data;
    }
}
   public void print(GenericType<Fruit> fruitGenericType){
        System.out.println(fruitGenericType.getData().getColor());

    }

如下调用编译是不通过的:

//此种调用允许
GenericType<Fruit> genericType = new GenericType<>();
        print(genericType);

//此种调用不允许
GenericType<Orange> orangeGenericType = new GenericType<>();
        print(orangeGenericType);

为了解决如上问题,引入了类型通配符 “?”

? extends X 表示类型的上界,类型参数是X的子类

? super X 表示类型的下界,类型参数是X的超类

这两种 方式从名字上来看,特别是super,很有迷惑性,下面我们来仔细辨析这两种方法。

? extends X

表示传递给方法的参数,必须是X的子类(包括X本身)

  public void print2(GenericType<? extends Fruit> fruitGenericType){
        System.out.println(fruitGenericType.getData().getColor());
    }

如下调用则可以通过:

GenericType<Orange> orangeGenericType = new GenericType<>();
        print2(orangeGenericType);

但是对泛型类GenericType来说,如果其中提供了get和set类型参数变量的方法的话,set方法是不允许被调用的,会出现编译错误,get方法则没问题,会返回一个Fruit类型的值。

为何?

道理很简单,? extends X 表示类型的上界,类型参数是X的子类,那么可以肯定的说,get方法返回的一定是个X(不管是X或者X的子类)编译器是可以确定知道的。但是set方法只知道传入的是个X,至于具体是X的那个子类,不知道。

总结:主要用于安全地访问数据,可以访问X及其子类型,并且不能写入非null的数据。

? super X

示传递给方法的参数,必须是X的超类(包括X本身)

但是对泛型类GenericType来说,如果其中提供了get和set类型参数变量的方法的话,set方法可以被调用的,且能传入的参数只能是X或者X的子类,get方法只会返回一个Object类型的值。

? super X 表示类型的下界,类型参数是X的超类(包括X本身),那么可以肯定的说,get方法返回的一定是个X的超类,那么到底是哪个超类?不知道,但是可以肯定的说,Object一定是它的超类,所以get方法返回Object。编译器是可以确定知道的。对于set方法来说,编译器不知道它需要的确切类型,但是X和X的子类可以安全的转型为X。

总结:主要用于安全地写入数据,可以写入X及其子类型。

限定类型变量 extend
   public <T extends Comparable> T compare(T a,T b){
        if(a.compareTo(b)>0) return a;else return b;
    }

T为Comparable的子类,extend的左右均可以有多个,且可以为类或者接口,但是如果是具体的实现类必须放在右面的第一个,且只能extend一个类:

public <T,K extends Comparable & Serializable> T compare(T a, T b)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值