Java初识泛型

  1. 什么是泛型
    泛型是jdk1.5引入的新语法,泛型就是适用于许多许多类型,就是对类型实现了参数化
  2. 实现一个类,类中包含一个数据成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值
class MyArray {
    public Object[] obj;
//此时存储数据的时候,什么类型都可以存储
//此时获取数据的时候,必须强转
//思考:能不能指定放啥类型,这样就不要强转了——在类的后面加<>,并指定类型
    public MyArray() {
        obj = new Object[10];
    }

    public void setValue(int pos, Object value) {
        obj[pos] = value;
    }

    public Object getValue(int pos) {
        return obj[pos];
    }

}


public class Test {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setValue(1,12);
        myArray.setValue(0,"123");

        int a = (int)myArray.getValue(1);
    }
}

这样写:
①此时存储数据的时候,什么数据都可以存储【比如int类型和String类型都可以存进去】

        myArray.setValue(1,12);
        myArray.setValue(0,"123");

②此时获取数据的时候,必须强制类型转换

        int a = (int)myArray.getValue(1);

思考:能不能指定放啥类型,然后就不需要强制类型转换了呢?——泛型!!!

(虽然此时当前数组可以存放任何数据,但是我们还是希望数组只能持有一种数据类型,而不是同时持有这么多类型)所以,泛型的主要目的是:指定当前容器要持有什么类型的对象,让编译器去做检查。把类型作为参数传递,需要什么类型,就传入什么类型。

  1. 将上述程序修改成泛型类
class MyArray<E> {
    public E[] obj = (E[]) new Object[10];

    public void setValue(int pos, E value) {
        obj[pos] = value;
    }

    public E getValue(int pos) {
        return obj[pos];
    }

}


public class Test {
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<>();
        myArray.setValue(0,1);
        int a = myArray.getValue(0);
        System.out.println(a);
    }
}

①此时只有指定的数据类型才可以存储

        MyArray<Integer> myArray = new MyArray<>();
        myArray.setValue(0,1);
        myArray.setValue(1,"abc0");//报错

因为此时在实例化对象的同时,指定了当前泛型类的指定参数类型是Integer【并且中鼎的参数类型必须是引用类型,放int就错了,int是基本数据类型】
②此时获取0下标存放的值没有发生强制类型转换

        int a = myArray.getValue(0);
        System.out.println(a);

但是这里发生了自动装箱和拆箱操作。

因此,泛型的好处/意义:
1.存储数据的时候,可以帮我们进行自动的类型检查
2.获取元素的时候,可以帮我们进行类型转换
【注意以上2个好处/意义都是发生在编译的时候,泛型是编译时期的一种机制,在运行的时候没有泛型的概念。】

  1. 泛型的语法
    ①定义泛型类
class 泛型类名称<类型形参列表>{

}

注意:
1、类名后的<>,比如代表占位符,表示当前类是一个泛型类,只相当于一个形参,实参是在实例化泛型类的时候给的。
2、不能new泛型类型的数组
规定:泛型当中不能去实例化一个泛型类型数组

T[] t = new T[5]; //不能实例化泛型数组

T[] t = (T[])new Object[5]; //对的,实例化Object类型数组,并且强制类型转换成泛型,但是不是足够好


//这种方法是最好的
Object[] obj = new Object[5]; //还是new Object对象
public T getPos(int pos) {
	return (T)obj[pos];  //在getset中再强转成T类型


②实例化泛型类对象

	MyArray<Integer> list = new MyArray<Integer>();
	泛型类<类型实参> 变量名      泛型类<类型实参>(构造方法实参)

注意:泛型只能接受类,类型实参所有的基本数据类型必须使用包装类。
关于省略类型实参:当编译器可以根据上下文推导的时候可以省略new后面的那个泛型实参

	MyArray<Integer> list = new MyArray();//可以推导出实例化需要的类型实参为Integer

③裸类型(左右两边的实参都省略了)
裸类型是一个泛型类但是没有带类型实参。

MyArray list = new MyArray();

理论上应该要报错,但是这里牵扯到Java的历史原因,裸类型是为了兼容老版本的API保留的机制。

  1. 泛型再Java中是怎么编译的?——擦除机制
    查看泛型程序的字节码文件
    在这里插入图片描述
    可以发现,在编译的过程中,将所有的E都替换成了Object,这种机制也叫擦除机制。
    擦除机制:运行的时候没有泛型,编译完成之后,泛型类型被擦除为Object类型,也就是说编译好之后E其实是一个Object。
    Java的泛型机制是在编译级别实现的,编译器生成的字节码在运行期间不包含泛型的类型信息。
    那么泛型只存在在编译的时候,并且在这个时候干了2件伟大而具有意义的事:【这也是和直接写Object的区别,泛型的好处】
    ①检查:检查是不是要的类型
    ②转换:去除了强制类型转换

  2. 泛型的上界
    泛型如果没有边界的话,都变成了Objetct。因此有时需要对传入的类型变量做一定的约束,通过类型边界来约束。(用到extends)
    ① 泛型的上界是类
    <E extends Number>
    代表E是Number的子类,或者E是Number本身

class A<T extends Number> {
    public T[] obj = (T[])new Object[10];
}
public class Test {
    A<Integer> a1 = new A<>();
    A<Double> a2 = new A<>();
    A<String> a3 = new A<>();  //报错
}

如代码A<String> a3 = new A<>(); 会编译错误,因为String不是Number的子类类型。

②泛型的上界是接口
<E extends Comparable<E>>
代表将来指定的参数E类型一定要实现了Comparable这个接口

class AA<E extends Comparable<E>> {

}

class BB {

}

public class demo {
    AA<Integer> a1 = new AA<>();//不报错,因为Integer里面实现了Comparable接口
    AA<BB> a2 = new AA<>();//报错,因为BB类没有实现Comparable接口
}

代码AA<BB> a2 = new AA<>();会编译错误,因为BB类没有实现Comparable接口,但是Integer不会,因为Integer包装类实现了Comparable接口。
在这里插入图片描述
7. 泛型也可以用在方法中
在返回值类型前加<>类型参数列表

    public <E>void swap(E[] array, int i, int j) {
        E tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

静态方法的泛型,需要在static后面<>声明泛型类型参数

    public static <E> void swap(E[] array, int i, int j) {
        E tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

关于调用泛型方法:
使用类型推导和不使用类型推导

    public static void main(String[] args) {
        Test test = new Test();
        Integer[] array = {1,2,3};
        test.swap(array,1,2); //使用类型推导
        test.<Integer>swap(array,0,1);//使用类型推导
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeKnightShuai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值