Java的泛型

为什么用泛型

早期在Object类型中是可以接受任意的对象类型,但是在实际应用中,肯定会有类型的转换问题。也就是存在这一个问题,所以Java就提供了泛型这个感念来解决这个安全问题。

什么是泛型

● 泛型,即就是“参数化类型”。一提到参数,最熟悉的就是定义为方法时的有形参数,然后调用此方法时实现传递参数。

如何理解“参数化类型”?
就是将数据的类型由原来的一个具体类型变为一个参数,类似于在调用方法的时候,对所需要传入的数据进行参数的传递。
然后在使用这个方法的时候传入具体的所需的类型即可(类型实参)。

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

● Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,泛型的好处就是在编译的时候能够检查类型安全。
泛型只在编译阶段有效

泛型类

先举个例子:
我们都知道ArrayList是可以存放任意类型的。那么我吗如下写:

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

public class Demo {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("asd");
        list.add(12);

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

执行这个代码,是肯定会报错的。报错的说明时,数据类型转换存在问题
在这里插入图片描述
为了解决类似这样的问题(在编译阶段就可以解决),对应的泛型也就应运而生。
所以我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。

在这里插入图片描述
在编译期间就会报错。

泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开 放相同的接口。

举例:一个普通的泛型类

//T可以为任意标识符,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Demo<T> {

    private T key;
    //key的数据类型为T,由外部所决定。和在调用Demo这个方法的时候,调用的是同一个类型

    private Demo(T key) {//泛型构造方法形参key的类型也为T,T的类型由外部指定
        this.key = key;
    }

    public T getKey() {//泛型方法getKey的返回值类型为T,T的类型由外部指定
        return key;
    }
}

在调用这个类:

public class Test {
    public static void main(String[] args) {

        Demo<String> stringDemo = new Demo<String>("hello gu cheng");
        Demo<Integer> integerDemo = new Demo<Integer>(123);

        System.out.println(stringDemo.getKey());
        System.out.println(integerDemo.getKey());
    }
}

在这里插入图片描述

注意:
1.泛型的类型参数只能是类类型,不能为int,double等这些的。
2.泛型的类型参数可以有多个。
3.如果没有定义具体类型,默认为Object。
4.不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。
if(right instanceof left<String>){ }

这里对于常见的泛型模式,推荐的名称是,也是程序员之间默认的一种格式:
K — 键,比如映射的键。
V — 值,比如 List 和 Set 的内容,或者 Map 中的值。
E — 异常类。
T — 泛型。

从泛型类派生子类

父类是泛型类的话:

  1. 子类也是泛型类的话,就要求子类的泛型是必须和父类的泛型类型是一致的

class A<T> extends Demo<T>

  1. 子类不是泛型类的话,父类就要明确泛型的数据类型。

class A extends Demo<String>

泛型接口

泛型接口与泛型类的定义及使用基本相同。

子类也是泛型类,子类和父类的泛型类型要一致。

class A<T> implements Demo<T>{ }

子类不是泛型类,父类要明确泛型的数据类型

public class A implements Demo<String> { }

总结一下这两点:
父类/父接口 如果是有泛型的,
那么子类继承/实现后 就有两种选择

  1. 如果子类没有泛型,那么父类/接口就要在编译期间指明泛型的类型
  2. 子类和父类/接口 都仍然是有泛型的。

代码演示:
子类也是泛型类:

public class User<T> implements Comparable<T>{

    @Override
    public int compareTo(T o) {
        return 0;
    }

    public static void main(String[] args) {
        new User<String >();
    }
}

子类不是泛型类:

public class User implements Comparable<User>{

    @Override
    public int compareTo(User o) {
        return 0;
    }
}

泛型通配符

通配符,表示的就是某一类的
类型通配符一般是使用"?"代替具体的类型实参。
所以,类型通配符是类型的实参,而不是形参

演示:

public class Demo1<T> {
    public void test(Demo1<?> demo){
    }

    public static void main(String[] args) {
        Demo1<Integer> demo11 = new Demo1<>();
        Demo1<Number> demo12 = new Demo1<>();

        demo11.test(demo11);
        demo12.test(demo12);
    }
}

?只能出现在参数的列表中见到
? 表示实际参数可以任意的传进去。

在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。

类型通配符上限

● 为泛型添加上边界,即传入的类型实参必须是指定类型的子类型

类/接口<?extends 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的子类类型。
在这里插入图片描述在这里插入图片描述

类型通配符下限

和上限一样的道理,类/接口<?super 实参类型>

?super T:可以接收T类型或者T的父类型对象。

要求该泛型的类型,只能是实参类型,或实参类型的父类类型。
在这里插入图片描述
在这里插入图片描述

类型擦除

因为泛型这个概念是在Java 1.5 版本之后才提出的感念,在这之前是没有泛型这个感念的,但是我们可以看出,有泛型的代码是可以很好的和之前版本的代码兼容起来的,这是因为,泛型这个信息是只存在于代码的编译期间,在进去jvm之前,代码在编译的期间,会将与泛型相关的信息全部擦除掉。我们就将这一现象叫做“类型擦除”。
泛型类型被擦除后,相应的类型就会被替换为Object类型或者上限类型的。
到最底层就都是Object类型了。

例子:(这里运用了java的反射)

public class Demo2<T> {

    T name;

    public Demo2(T name) {
        this.name = name;
    }

    public static void main(String[] args) throws NoSuchFieldException {
        Demo2<String> demo = new Demo2<String>("xin cheng");
        //这里定义了泛型的类型为String类型

        Class c = demo.getClass(); //这里通过反射来获取当前运行时的状态信息
        Field name = c.getDeclaredField("name");

        System.out.println(name.getName() + "::" + name.getType());
        //这里输出发现当前类型为Object类型
    }
}

在这里插入图片描述

所以泛型的意义就是,方便人们在写代码的时候,合理控制参数的传递。到底层运行的时候,就又都会转为Object类型来存储。

了解:>> Java的反射

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值