深入理解Java中的泛型以及案例演示

1 泛型

  诞生的必要性:Java1.4或更早版本,原来List集合当中可以存放任何类型,当对集合当中的元素进行统一操作的时候,容易引发ClassCastException异常。当存在各种数据类型的时候,不利于对集合的元素进行统一操作。
  解决思路:像数组一样,只能存放某种数据类型。
  此时在JDK1.5 添加了一个新技术:泛型。

1.1 定义

  泛型,即“参数化类型”。顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型变量),然后在使用/调用时传入具体的类型(类型实参)。这种类型变量可以用在类、接口和方法的创建中。当然最常见的就是用在集合中。
  设定的语法:类名<类型变量> 也叫参数化类型
  类型变量:使用大写的形式,只能是引用类型,不能使基本数据类型。Java中,用变量E表示集合的元素类型,K和V分别表示表的key与value类型,T表示“任意类型”。
  用具体的类型替换类型变量就可以实例化泛型类型。

1.2 作用

  1. 将运行时期的异常提前到了编译时异常(classcastexception),方便了元素的统一操,优化了程序结构。
  2. 泛型参数一旦确定,就规定了在类使用的时候,只能存放某一种数据类型。此时集合还能够记住集内元素的类型,从而无需对集合元素进行强制转换,使得程序更加简洁。

1.3 泛型类

把泛型定义在类上。格式:public class 类名<泛型类型1,…>

注意
  泛型类型必须是引用类型。
案例
在类上声明了一个不确定的类型(类型变量);在该类当中都可以使用该不确定的类型。

public class Genericity<T> {
    private T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }
}

使用泛型类

Genericity<String> tool = new Genericity<String>();
//使用String对T进行初始化。
tool.setObj("ggg");  

1.4 泛型方法

泛型可以定义在方法上:泛型方法。泛型声明在方法的返回值之前,并且可以与类名上的泛型声明不一致!

class ss<Q> {

    /**
     * 泛型方法,使用自己的泛型T
     *
     * @param t   T
     * @param <T> 新类型<T>的声明
     */
    public <T> void set(T t) {

    }

    /**
     * 泛型方法,使用类上的泛型Q
     *
     * @param q Q
     * @return Q 返回值的类型
     */
    public Q getXxx(Q q) {
        return q;
    }
}

注意
  当在类名之后声明了泛型时。如果想在该类静态方法使用和类一样的泛型变量,即使在类上已经声明了泛型,还是要像自定义泛型方法一样,类下面的静态方法的返回之前一定还要重新声明一次。但是非静态方法就能直接使用同名泛型变量,不用再次声明!案例如下:

public class GenericDemo01<T> {

    //普通方法使用与类同名泛型,不需要声明
    //传递T类型参数,直接使用
    public void get1(T t)
    {

    }
    //返回T类型,可以直接使用
    public T get2()
    {
        return null;
    }

    //静态方法使用同名泛型,和自定义泛型方法一样,必须声明
    //想要返回T类型或传递T类型的参数,则必须在返回之前重新声明
    public static <T> void get3(T t)
    {

    }
    //想要返回T类型或传递T类型的参数,则必须在返回之前重新声明
    public static <T> T get4()
    {
        return null;
    }
}

使用细节

List list=New ArrayList();
List list=New
ArrayList(); List list=New ArrayList(); //泛型推断.JDK1.7

以上三种写法都可以。但是前后声明的泛型类型必须要相同.泛型只能存放引用类型,在JDK1.5之后建议使用泛型,否则会报警告。

1.5 泛型接口

1.5.1 泛型接口的定义

需求: 对学生进行增删改查操作:

  interface StudentDao{
    //增加:
     void add(Student stu);
     //删除:
     boolean delete(String id);
     //修改:
     boolean update(Student stu);
     //查询: 唯一性查询:
     Student findById(String id);
  }

  设定一个具体的实现类: 对接口当中的方法进行具体的实现,那么针对一个对象就需要设定一套接口,并且设定一套实现类:
  Teacher : 设定一套接口, 设定一套实现类:
  Person : 设定一套接口, 设定一套实现类:
  这样就比较麻烦了,因此可以使用泛型接口。

1.5.1 泛型在接口上的使用

使用泛型接口可以减少Dao接口的设计数量。

  interface Dao<T>{
    void add(T t);//增加
	
	boolean delete(String id); //删除
	
	boolean update(T t);
	
	T findById(String id);
  } 

1.6 泛型通配符以及上、下限泛型

Collection<?> ?:可以代表任意类型

Collection<?> c5 = new ArrayList<Object>();
Collection<?> c6 = new ArrayList<Animal>();
Collection<?> c7 = new ArrayList<Dog>();
Collection<?> c8 = new ArrayList<Cat>();

1.6.1 上限泛型

Collection<? extends E> ? 只能是E类型 或者是E 类型的子类(否则编译报错):这样的泛型称之受限泛型:上限泛型。
一般在存储元素时使用上限(? extends E)。因为一旦确定好类型,存入的就都是E或E的子类,取出时都按照上限类型E来运算,不会出现类型安全隐患

Collection<? extends Animal> c9 = new ArrayList<Object>();
Collection<? extends Animal> c10 = new ArrayList<Animal>();
Collection<? extends Animal> c11 = new ArrayList<Dog>();
Collection<? extends Animal> c12 = new ArrayList<Cat>();

1.6.1 下限泛型

Comparator<? super E> ? 只能是E类型或者是E类型的父类型(否则编译报错):受限泛型的下限泛型。(一般只有比较器用到)

        //min比较器
        min(Collection<? extends T> coll, Comparator<? super T> comp)
        //sort排序比较器
        sort(List<T> list, Comparator<? super T> c)
                
        Collection<? super Animal> c13 = new ArrayList<Object>();
        Collection<? super Animal> c14 = new ArrayList<Animal>();
        Collection<? super Animal> c15 = new ArrayList<Dog>();
        Collection<? super Animal> c16 = new ArrayList<Cat>();

1.7泛型擦除

  Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List和List等类型,在编译之后都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。这样做的目的,是确保能和Java 5之前的版本开发二进制类库进行兼容。
  类型擦除的基本过程也比较简单,首先是找到用来替换类型参数的具体类。这个具体类一般是Object。如果指定了类型参数的上界的话,则使用这个上界。把代码中的类型参数都替换成具体的类。

ArrayList<String> list1 = new ArrayList<String>();
list1.add("abc");
ArrayList<Integer> list2 = new ArrayList<Integer>();
list2.add(123);
//将返回true
System.out.println(list1.getClass() == list2.getClass());
发布了58 篇原创文章 · 获赞 105 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43767015/article/details/105135473