Java八股文系列六:泛型

image

一、泛型的基本概念

泛型的本质是参数化类型,就是说所操作的数据类型被指定为一个参数。泛型提供了编译时类型的安全检查机制,该机制允许程序在编译时检测非法的类型。在不使用泛型的情况下,可以引用Object类型来实现参数的任意化,但是在具体使用时需要进行强制类型转换,如果不知道参数具体的引用类型会引起类型转换错误,而这种错误只有在运行时才出现。使用泛型后就可以在编译期检测类型错误,提高了程序的安全性。下面看一个例子解释泛型的好处。

使用泛型前:

    public static void main(String[] args) {
        List a = new ArrayList();
        a.add("name");
        a.add(1);
        a.add(BigDecimal.ONE);
        BigDecimal bigDecimal = (BigDecimal) a.get(2);
        bigDecimal.add(BigDecimal.ONE);
    }

要想使用BigDecimal的方法,就必须对列表中的元素进行显示的强制类型转换。

使用泛型后:

   public static void main(String[] args) {
        List<BigDecimal> a = new ArrayList();
        a.add("name");//产生编译错误
        a.add(1);//产生编译错误
        a.add(BigDecimal.ONE);
        a.get(2).add(BigDecimal.ONE);
    }

使用泛型后,规定在List中只能存放BigDecimal的元素,存放其他类型的元素会在编译时会产生错误,不需要显示的进行强制类型转换。

二、泛型标记和泛型限定

  • E-Element 在集合中使用,表示在集合中存放的元素
  • T-Type 表示Java类
  • K-Key 表示键
  • V-Value 表示值
  • N-Number 表示数值类型
  • ? 表示不确定的类型

对泛型的上限限定:<? extends T>,表示该通配符所代表的类型是T的子类或是T的实现类。

对泛型的下限限定:<? super T>,表示该通配符所代表的类型是T的父类或父接口。

三、类型擦除

在编码阶段使用泛型时加上的参数类型,会被编译器在编译时去掉,这个过程叫做类型擦除。

    public static void main(String[] args) {
        ArrayList<String> arrayString=new ArrayList<String>();
        ArrayList<Integer> arrayInteger=new ArrayList<Integer>();
        System.out.println(arrayString.getClass()==arrayInteger.getClass());
    }

输出:

true

ArrayList和ArrayList被编译之后统一为List,编译器擦除了String和Integer的信息。

Java类型擦除的过程:
首先找到用来替换类型参数的具体类,然后把代码中的类型参数都替换为具体的类。

既然类型都被擦除了,那么泛型又是如何做到像ArrayList泛型类型,不允许向其中插入String对象的呢?

Pair<Integer> pair=new Pair<Integer> ();
pair.setValue(3);
Integer integer=pair.getValue();
System.out.println(integer);

擦除类型后getValue()将返回Object类型,编译器会对Object类型进行强制类型转换,转换成Integer类型。也就是说编译器会把getValue()方法翻译成两条字节码指令:

  1. 对原始方法Pair.getValue的调用,将返回Object类型。
  2. 将返回的Object类型强制转换为Integer。

四、泛型的使用

4.1 泛型方法

将方法的参数定义为泛型。

    public static void main(String[] args) {
        test(1, "a");
    }

    public static <T> void test(T... array) {
        for (T e : array) {
            if (e instanceof Integer) {
                System.out.println("Integer");
            } else if (e instanceof String) {
                System.out.println("String");
            }
        }
    }

4.2 泛型类

可以传入不同的参数类型实例化不同的对象。

public class General<T> {
    private T t;
    public T get() {
        return t;
    }
    public void set(T t) {
        this.t = t;
    }

    public static void main(String[] args) {
        General<String> general = new General<>();
        general.set("a");
        General<Integer> general1 = new General<>();
        general1.set(1);
    }
}

4.3 泛型接口

public interface IGeneral<T> {
    T get();
}
public class IGeneralImpl implements IGeneral<Integer> {
    @Override
    public Integer get() {
        return 1;
    }
    public static void main(String[] args) {
        new IGeneralImpl().get();
    }
}

五、泛型常见的面试题

5.1 Java中的泛型是什么?有什么好处?

泛型是一种参数化类型的机制。它使得代码适用于各种类型,从而编写出更加通用的代码。泛型是一种编译时类型检查机制。

5.2 可以把List传递给一个接受List参数的方法吗?

不可以,这样会有编译错误。因为List可以存储任何类型的对象,而List只能存储String类型。

5.3 Java中List和原始类型List之间的区别?

最本质的区别就是,在编译时编译器不会对原始类型的做类型检查。可以把任何带参数的泛型类型传递给接受原始类型List的方法,但却不能把List传递给接受List的方法。

5.4 List<?>和List的区别?

List<?>是未知类型的List,List是任意类型的List。可以把List赋值给List<?>,却不能把List赋值给List。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值