Java 中的泛型

引言

当我们不使用泛型的时候

当我们创建一个 Object 类型数组的时候,我们往里面放什么类型的元素都可以,但是,当我们拿出元素的时候,需要重新定义变量接收,此外,我们还需要在拿出数据的时候,进行强制类型转换。因为 Object 类是所有类型的父类,而当我们放进去可以兼容,而拿出来的时候,需要考虑类型,因为我们需要使用它。

程序清单1:

class MyArray{
    public Object[] objects = new Object[10];

    public void set(int pos, Object val){
        objects[pos] = val;
    }
    public Object get(int pos){
        return objects[pos];
    }
}
public class Test1 {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.set(0,"abc");
        myArray.set(1,123);
        String str = (String) myArray.get(0);//需要强制转换
        int x = (int)myArray.get(1);//需要强制转换
        System.out.println(str);
        System.out.println(x);
    }
}

一、引入泛型

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

程序清单2:

/**
 * <T> 此时代表当前类是一个泛型类,T: 代表一个占位符
 */

class MyArray1<T>{
    //public T[] objects = new T[10]; //error, //1
    public T[] objects = (T[])new Object[10];
    public void set(int pos, T val){
        objects[pos] = val;
    }
    public T get(int pos){
        return objects[pos];
    }
}

public class Test2 {
    public static void main(String[] args) {
        MyArray1<String> myArray1 = new MyArray1<>(); //2
        myArray1.set(0,"abc");
        //myArray1.set(1,123); //error //3
        String str = myArray1.get(0); //4
        System.out.println(str);
    }
}
// 注释1: 不能实例化一个泛型的数组
// 注释2: 类 MyArray1 指定了当前类型为 String
// 注释3: val 需要 String 类型
// 注释4: 拿变量接收的时候,不再像程序清单1中需要强制类型转换

1. 擦除机制

在编译的过程当中,将所有的T替换为 Object 这种机制,我们称为:擦除机制。
Java 的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息。

分析

2. 泛型的意义

① 自动对类型进行检查
② 自动对类型进行强制类型的转换

二、泛型的上界

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。

1. 语法格式

//语法格式
class 泛型类的名称<类型形参 extends 类型边界> {

}

//举例
public class MyArray<E extends Number> {

}

2. 例子

程序清单3:

class MyArray2<T extends Number>{

}
public class Test3 {
    public static void main(String[] args) {
        MyArray2<Integer> test1 = new MyArray2<>();
        MyArray2<Number> test2 = new MyArray2<>();
        //MyArray2<String> test3 = new MyArray2<>();//error
    }
}

说明:上面的 T 只能是 Number 或 Number 的子类。
包装类 Integer 是 Number 的子类
包装类 String 不是 Number 的子类

三、泛型的比较

1. 语法格式

public class MyArray<E extends Comparable<E>> {

}

程序清单4:

class Alg<T extends Comparable<T>> {

    //因为 T 是泛型,我们不知道比较的是引用类型还是整型、还是浮点型...
    //所以我们可以确定类型边界 Comparable
    //而 Integer 和 String 类这些都实现了 Comparable 接口
    public T findMax(T[] array){
        T max = array[0];
        for (int i = 0; i < array.length; i++) {
            if(max.compareTo(array[i]) < 0){
                max = array[i];
            }
        }
        return max;
    }
}
public class Test4 {
    public static void main(String[] args) {
        Alg<Integer> alg1 = new Alg<>();
        Integer[] array1 = {1,3,7,5};
        System.out.println(alg1.findMax(array1));

        Alg<String> alg2 = new Alg<>();
        String[] array2 = {"he", "she", "nice"};
        System.out.println(alg2.findMax(array2));
    }
}

输出结果:

out

四、通配符

通配符就是一个问号的标识 " ? "
通配符的存在是用来解决泛型无法协变的问题的。

我们可以这样理解:泛型 T 就像一个变量,等着你来传一个具体的类型,而通配符更像是一种规定,规定你只能传哪些参数。

1. 通配符上界

语法:

<? extends 上界>
<? extends Number>//可以传入的实参类型是Number或者Number的子类

2. 通配符下界

语法:

<? super 下界>
<? super Integer>//代表 可以传入的实参的类型是Integer或者Integer的父类类型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

十七ing

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

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

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

打赏作者

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

抵扣说明:

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

余额充值