Java泛型

一、定义

泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。

格式:<数据类型>

细节:泛型只能支持引用数据类型,基本数据类型需要变成其包装类。


question:在JDK5之前,没有泛型的时候,集合如何存储数据?

我们注意到,如果没有泛型,add 方法的形参为 Object 类型。

所以使用 add 方法可以添加任意类型的元素。

public class Demo {
    public static void main(String[] args) {
        //1.创建集合
        ArrayList list = new ArrayList();

        //2.添加数据
        list.add(123);
        list.add("aaa");
        list.add(true);

        //3.遍历集合的每一个元素
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Object obj = it.next();
            System.out.println(obj);
        }
    }
}

由于添加的数据都是Object类的直接或者间接子类,所以我们可以使用多态的方式,获取数据。

但我们知道,多态的缺点:不能访问子类的特有功能。

比如我想调用 String 类的一些成员方法,比如获取字符串的长度时,则无法调用。

虽然我们可以使用强制类型转换,但由于添加的数据是任意类型的。

对于该集合中,Integer 和 Boolean 类型的元素则会发生类型转换异常。

因此,JDK5推出了泛型。

泛型的好处:

① 统一数据类型

② 把运行时期的问题提前到了编译时期,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来。


Tips:Java中的泛型是伪泛型(编译的时候限定类型,编译之后底层存的事实上还是Object类型取的时候会将 Object 类型强转成限定的数据类型)。

 这种过程被称为   “泛型的擦除”。

question:为什么要这样设计呢?

在JDK5以前,java已经写了很多的代码了,这些代码市面上也有很多人用了。

所以,我们不能直接改动 java 底层的代码,这样所有人的代码都要发生变化。

但可以使用伪泛型给它加一个限定,这样既不用修改之前的代码,也可以进行类型的统一。


 注意点:

泛型中不能写基本数据类型(伪泛型使得底层还是 Object 类型,而基本数据类型无法转变成Obejct 类型)。

指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型

如果不写泛型,类型默认是Object 类型

二、泛型的使用

泛型可以在很多地方进行定义

1.泛型类

使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类。

这里的 E 可以理解为一个变量,但不是用来记录数据的,而是记录数据的类型。 

如果有多个变量类型不确定,但属于不同的泛型,可以加逗号,如 <E,T>

泛型类:

public class MyList<E> {
    final int len = 10;
    Object[] obj = new Object[len];
    int size;

    public boolean add(E e) {
        if (size >= len) {
            return false;
        }
        obj[size] = e;
        size++;
        return true;
    }

    public E get(int index) {
        //底层取的时候要进行强制转换
        return (E) obj[index];
    }

    //重写toString方法,使其打印的不是地址值,而是属性值
    @Override
    public String toString() {
        //将数组变为字符串
        return Arrays.toString(obj);
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        MyList<String> list1 = new MyList<>();
        list1.add("aaa");
        list1.add("bbb");
        list1.add("ccc");
        System.out.println(list1);
        //[aaa, bbb, ccc, null, null, null, null, null, null, null]

        MyList<Integer> list2 = new MyList<>();
        list2.add(1);
        list2.add(2);
        list2.add(3);
        System.out.println(list2);
        //[1, 2, 3, null, null, null, null, null, null, null]
    }
}

2.泛型方法

方法形参类型不确定时,可以使用类名后面定义的泛型<E>。

但是如果类中只有一个方法的形参时不确定,就没必要在整个类中定义泛型了。

此时就可以在方法声明上定义泛型。

这里的 T 同样可以理解为一个变量,但不是用来记录数据的,而是记录数据的类型。

区别:

泛型类的话,类中的所有方法都可以用该泛型;但泛型方法的话,只有本方法可以使用。


需求:定义一个集合工具类,类中有一个泛型方法 addAll,可以添加多个数据。

//工具类
public class ListUtil {
    //私有化构造方法
    private ListUtil() {
    }

    //...表示可变参数(一个参数也行,多个参数也可以)
    public static <E> void addAll(ArrayList<E> list, E... e) {
        for (E element : e) {
            list.add(element);
        }
    }

}

 测试类:

public class Test {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        ListUtil.addAll(list1, "aaa", "bbb", "ccc");
        System.out.println(list1);//[aaa, bbb, ccc]

        ArrayList<Integer> list2 = new ArrayList<>();
        ListUtil.addAll(list2, 1, 2, 3, 4);
        System.out.println(list2);//[1, 2, 3, 4]
    }
}

3.泛型接口

当一个接口当中变量类型不确定时,就可以定义泛型接口。

重点:如何使用一个带泛型的接口呢?

 (1)方式一

List 本身是一个泛型接口

定义一个实现类,给出数据类型。一旦给出后,类型就确定了。

 测试类:

实现类已经给处数据类型,此时数据类型就已经限定了,只能添加该数据类型的元素了。

(2)方式二 

实现类继续延续泛型,不给出确定的数据类型。

 测试类:

由于实现类并没有限定类型,所以只能在创建对象时给泛型限定类型。

 

三、泛型的继承和通配符

1.泛型的“继承”  

注:泛型不具备继承性,但数据具备继承性。

在 method 方法中,形参已经限定泛型为 Ye 类型。那么调用 method 方法传入的集合的元素数据了类型也只能是 Ye 类型,Ye 的子类也不行,因为泛型不具有继承性


但数据具有继承性,前面说了, 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型。 

 注意区分:一个是传递整个限定数据类型的集合作为数据,一个是往限定数据类型的集合里面添加数据。

2.通配符

需求:定义一个方法,形参是一个集合,集合中的数据类型不确定,但数据类型属于某个继承体系。即:集合的类型可以是 Ye,Fu,Zi,但不能是 String,Integer等。

如果使用前面的泛型方法,可是实现数据类型不确定,但缺点是可以接收任意的数据类型,String 也可以。

注:

 只写一个 ? 也表示任意数据类型,和 <E> 没有区别,只是修饰符后面不需要再加 ? 了而已。

② 写了通配符 ? 后,extend 或者 super 的 E ,就必须限定数据类型。

这里并没有限定集合的数据类型,而是限定了数据类型的范围。 

关键点:通配符可以限定数据类型的范围。

3.使用场景

① 定义类,方法,接口的时候,如果类型不确定,就可以定义泛型。

② 如果类型不确定,但是能知道属于哪个继承体系中,可以使用泛型的通配符。

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值