泛型的使用

1、什么是泛型

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时有实参那么参数化类型怎么理解呢?

顾名思义,就是将类型由原来的具体的类型参数化 (动词),类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),

然后在使用 / 调用时传入具体的类型(类型实参)。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中。

操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

参考:https://www.cnblogs.com/coprince/p/8603492.html

1.1、常见的泛型类型变量

E:元素(Element),多用于 java 集合框架 

K:关键字(Key) 

N:数字(Number)

T:类型(Type) 

V:值(Value)

二、为什么要使用泛型

我们先来举两个例子

package com.nc.geneeric;

import java.util.ArrayList;
import java.util.List;

public class Test {

    public static void main(String[] args) {

        List list = new ArrayList();

        list.add("泛型测试");

        list.add(666);

        for(int i = 0; i < list.size(); i++){
            String str = (String)list.get(i);
            System.out.println(str);
        }
    }
}

本身这个 list 是要装载 String 去打印的,但是大家发现没有?我传入 int 类型时(编译期),Java 是没有任何提醒的(顶多是 IDEA 警告)。直到我循环调用(运行期)打印方法,打印 int 类型时,Java 才报错:

泛型测试
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
	at com.nc.geneeric.test.main(test.java:17)

第二个例子,咱们来实现一个可以操作各种类型的加法,如下代码:

package com.nc.geneeric;

public class Addition {

    static int add (int a, int b){
        System.out.println(a + "+" + b + "=" + (a+b));
        return a+b;
    }
    static float add (float a, float b){
        System.out.println(a + "+" + b + "=" + (a+b));
        return a+b;
    }
    static double add (double a, double b){
        System.out.println(a + "+" + b + "=" + (a+b));
        return a+b;
    }

    static <T extends Number>double addition(T a, T b){
        System.out.println(a + "+" + b + "=" + (a.doubleValue()+b.doubleValue()));
        return a.doubleValue()+b.doubleValue();
    }

    public static void main(String[] args) {
        add(1, 2);
        add(1f, 2f);
        add(1d, 2d);
        System.out.println("--------------------------");
        addition(1, 2);
        addition(1f, 2f);
        addition(1d, 2d);
    }
}

这个加法可以操作 int、float、double 类型,但相应的也必须重写对应的加法,而此时用一个泛型方法就实现了上面三个重载方法的功能。

1+2=3
1.0+2.0=3.0
1.0+2.0=3.0
--------------------------
1+2=3.0
1.0+2.0=3.0
1.0+2.0=3.0

所以使用泛型原因有三个:

  • 提高可读性

  • 使 ClassCastException 这种错误在编译期就检测出来

  • 适用于多种数据类型执行相同的代码(代码复用)

参考:https://www.jianshu.com/p/986f732ed2f1

三、泛型的详解

3.1、泛型类

由我们指定想要传入泛型类中的类型,把泛型定义在类上,用户使用该类的时候,才把类型明确下来,比如:定义一个万能的实体数据暂存工具类。

注意:泛型类在初始化时就把类型确定了

package com.nc.bean;

public class EntityTool<T> {

    private T entity;

    public T getEntity() {
        return entity;
    }

    public void setEntity(T entity) {
        this.entity = entity;
    }

    public static void main(String[] args) {
        // 创建对象并指定元素类型
        EntityTool<String> stringTool = new EntityTool<>();
        stringTool.setEntity("泛型类");
        String s = stringTool.getEntity();
        System.out.println(s);


        // 创建对象并指定元素类型
        EntityTool<Integer> integerTool = new EntityTool<>();
        // 此时,如果这里传入的还是 String 类型,那就会在编译期报错
        integerTool.setEntity(10);
        int i = integerTool.getEntity();
        System.out.println(i);
    }
}

3.2、泛型方法

有时候我们只想在方法中使用泛型,可以这么定义:

值得注意的是:

  • 与泛型类不同,泛型方法在调用时才确定最终类型

  • 若有返回值,返回值不需要强转

package com.nc.geneeric;

public class Show {
    public static <T>T show(T t){

        System.out.println(t);
        return t;
    }

    public static void main(String[] args) {

        String s = show("泛型测试");
        int num1 = show(666);
        double num2 = show(666.666);
        System.out.println("--------------------------");
        System.out.println(s);
        System.out.println(num1);
        System.out.println(num2);
    }
}

3.3、泛型接口

泛型接口分两种实现方法:

一、实现类不明确泛型接口的类型参数变量,这时实现类也必须定义类型参数变量(比如下面 Showimpl)

接口:

package com.nc.geneeric;

public interface Show<T> {
    void show(T t);   
}

class ShowImpl<T> implements Show<T>{

    @Override
    public void show(T t) {
        System.out.println(t);
    }

    public static void main(String[] args) {
        
        ShowImpl<String> stringShow = new ShowImpl<>();
        stringShow.show("泛型接口测试");
        
    }
}

二、明确泛型接口的类型参数变量

class ShowImpl1 implements Show<String>{

    @Override
    public void show(String s) {
        System.out.println(s);
    }

    public static void main(String[] args) {

        ShowImpl1 showImpl = new ShowImpl1();
        showImpl.show("泛型接口测试");
        
    }
}

3.5 限定泛型类型变量

限定泛型类型上限

其实就是相当于指定了泛型类的父类声明类:类名 <泛型标识 extends 类>{}

在类中使用:

package com.nc.geneeric;

public class Show<T extends Number> {

    private T show(T t){

        System.out.println(t);
        return t;

    }

    public static void main(String[] args) {

        // 初始化时指定类型
        Show<Integer> show = new Show<>();
        show.show(6666666);

        // 会报错,该类只接受继承于 Number 的泛型参数
        // Show<String> stringShow = new Show<>();
    }



}

方法中使用:

定义对象:类名 <泛型标识 extends 类> 对象名称

package com.nc.geneeric;

public class Info<T> {
    // 定义泛型变量
    private T var;

    public void setVar(T var) {
        this.var = var;
    }

    public T getVar() {
        return this.var;
    }

    public String toString() {
        return this.var.toString();
    }
}

class ShowInfo{

    // 用在方法上,只能接收 Number 及其子类
    public static void showInfo(Info<? extends Number> t) {
        System.out.println(t);
    }

    public static void main(String args[]) {
        
        Info<Integer> i1 = new Info<>();
        Info<Float> i2 = new Info<>();
        i1.setVar(666);
        i2.setVar(666.666f);
        showInfo(i1);
        showInfo(i2);
        
    }
}

限定泛型类型下限

定义对象:类名 < 泛型标识 super 类 > 对象名称

与指定上限相反,指定下限定很简单,就是相当于指定了泛型类的子类,不再赘述。

class ShowInfo{

    // 只接收 String 的父类
    public static void showInfo(Info<? super String> t) {
        System.out.println(t);
    }

    public static void main(String args[]) {

        Info<String> strInfo = new Info<>();
        Info<Object> objInfo = new Info<>();
        strInfo.setVar("限定泛型类型下限");
        objInfo.setVar(new Object());
        showInfo(strInfo);
        showInfo(objInfo);

    }
}

3.6 通配符类型

  • <? extends Parent> 指定了泛型类型的上限

  • <? super Child> 指定了泛型类型的下限

  • <?> 指定了没有限制的泛型类型

3.7 泛型擦除

泛型是提供给 javac 编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的 java 程序后,生成的 class 文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为 “擦除”。

3.8 泛型的使用规范

1、不能实例化泛型类 

2、静态变量或方法不能引用泛型类型变量,但是静态泛型方法是可以的 

3、基本类型无法作为泛型类型 

4、无法使用 instanceof 关键字或 == 判断泛型类的类型 

5、泛型类的原生类型与所传递的泛型无关,无论传递什么类型,原生类是一样的 

6、泛型数组可以声明但无法实例化 

7、泛型类不能继承 Exception 或者 Throwable

 8、不能捕获泛型类型限定的异常但可以将泛型限定的异常抛出

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值