Java必知:泛型 - Go语言中文社区

Java必知:泛型


认识泛型

泛型的本质

泛型,即”参数化类型”或者”类型参数化”。
提到参数化,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。
那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在调用时传入具体的类型(类型实参)

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,我们要操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

案例:

我们知道ArrayList可以存放任意类型,但是如果我们使用泛型进行强制类型的限定和不限定的情况不一样。
不限定的情况:可以添加任意类型的值。
在这里插入图片描述
强制类型的限定时:我们在申明ArrayList时,指定了存储类型为String 所以在编译阶段,我们存入其他类型的元素,就会报出错误.即我们的申明强制的指定了ArrayList集合中存储的元素只能是指定的String类型.这就是泛型使用的本质意义所在,指定参数化类型.

在这里插入图片描述

泛型的好处

通过上述案例.我们不难分析出来泛型会给我们的代码带来如下好处:
1.编译期间即确定类型,保证类型安全,避免强制类型转换异常同时也规避了强制类型转换带来的问题。
2.代码利于重用,增加通用性。

问题:上面好处中提到了编译期间确定类型,那运行期间呢?泛型不适用在运行期间吗?我通过下面的内容给大家详细分析这个问题。

泛型的类型擦除

泛型只在编译阶段有效,泛型类型在逻辑上可看成是多个不同的类型,
但是其实质都是同一个数据类型编译之后程序会采取去泛型化的措施。

演示代码:不管你指定泛型类型还是没指定,最终在编译之后都同一类型。
在这里插入图片描述
问题:既然经编译后程序会采取去泛型化的措施,那么咱们就用反射机制动态添加一个非限定类型的元素来验证一下。
在这里插入图片描述

通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。

泛型通配符

? 通配符理解:
前面的内容.我们知道ArrayList 是典型的泛型类。即可以限制存储的元素的元素类型。ArrayList指定存入的元素是string。ArrayList指定存储元素为Integer类型.。但是如果只有我们使用的时候才能明确知道存入类型时,我们可以使用ArrayList<?> 来表示通用的类型。
无边界通配符:
代码演示:比如这种操作,是不是提高代码的复用性了。
在这里插入图片描述
上界通配符:
例如:? extends Number 代表从Number往下的子类或者孙类对象都是可以的。
在这里插入图片描述
下界通配符:
例如:? super Integer 代表从Integer往上的父类或者‘爷爷’类对象都是可以的。

泛型的使用

泛型跟我们的成员属性一样,需要先声明才能使用。泛型的声明采用 <> 进行声明。 声明一般约定采用单个大写字母表示。常用的有 K E
T V 等等字符。

错误案例:编译器报错,因为泛型只有使用,并没有声明。
在这里插入图片描述
正确案例:
在这里插入图片描述

泛型类

泛型类一般指泛型的定义与类名一起。在创建实体对象时,指定泛型的类型
使用泛型类,可以使类更加的灵活
演示代码:这只是一个demo不要纠结太多的细节。
Person2类:
在这里插入图片描述
Person2实例化:
在这里插入图片描述

泛型方法

方法的泛型有两种:
1.实体方法:实体方法可以使用在类中定义的泛型或者方法中定义的泛型。
2.静态方法:不可以使用在类中定义的泛型,只能使用在静态方法上定义的泛型。
演示代码:
在这里插入图片描述

泛型接口

指在接口的定义时进行泛型的声明。
接口是标准的指定者,指实现该接口的类必须实现其标准定义(即抽象方法)。
所以在接口上进行泛型的声明,或者说使用泛型接口,可以让我们的程序代码更加简洁,更加多变。
案例:
计算接口Count的定义

public interface Count {
    int add(int num1,int num2);
    int sub(int num1,int num2);
    int mul(int num1,int num2);
    int div(int num1,int num2);
}

计算接口的实现类Calculator

public class Calculator implements Count {
    @Override
    public int add(int num1, int num2) {
        return 0;
    }
    @Override
    public int sub(int num1, int num2) {
        return 0;
    }
    @Override
    public int mul(int num1, int num2) {
        return 0;
    }
    @Override
    public int div(int num1, int num2) {
        return 0;
    }
}

我们发现这样子的程序灵活度会很差。当前计算接口的实现者只能满足int类型数字计算,如果想满足其他数字类型的计算,需在接口中定义额外的方法。
此时,如果采用泛型接口即可完美解决问题。
计算泛型接口CalGeneric的定义:

public interface CalGeneric<C> {
    public C add (C num1,C num2);
    public C sub (C num1,C num2);
    public C mul (C num1,C num2);
    public C div (C num1,C num2);
}

计算泛型接口的实现类CalculatorGeneric,计算Double类型

public class CalculatorGeneric implements CalGeneric<Double> {
    @Override
    public Double add(Double num1, Double num2) {
        return null;
    }
    @Override
    public Double sub(Double num1, Double num2) {
        return null;
    }
    @Override
    public Double mul(Double num1, Double num2) {
        return null;
    }
    @Override
    public Double div(Double num1, Double num2) {
        return null;
    }
}

计算泛型接口的实现类CalculatorGeneric2,计算Float类型

public class CalculatorGeneric2 implements CalGeneric<Float> {
    @Override
    public Float add(Float num1, Float num2) {
        return null;
    }
    @Override
    public Float sub(Float num1, Float num2) {
        return null;
    }
    @Override
    public Float mul(Float num1, Float num2) {
        return null;
    }
    @Override
    public Float div(Float num1, Float num2) {
        return null;
    }
}
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_45240169/article/details/104273753
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢