Java泛型介绍

Java 泛型

Java 泛型(generics)是 JDK 5 中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时监测到非法的类型数据结构。

泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。

  • 类型安全: 编译期间检查类型
  • 无需强制类型转换

泛型类

定义

class 类名<泛型标识, ...泛型标识> {
    private 泛型标识 变量名;
}

// 示例
class Name<T> {
    private T name;
}

使用

类名<具体数据类型> 对象名 = new 类型<具体数据类型>();

// java 7 以后, 后面 <> 中的具体数据类型可以省略不写
类名<具体数据类型> 对象名 = new 类型<>();

// 示例
Name<String> name = new Name<>();
  • 泛型类在创建对象的时候,如果没有指定类型,默认为 Object 类型;
  • 泛型类不支持基本数据类型;
  • 同一泛型类,不同数据类型创建的对象,逻辑上可以看成是多个不同的类型,本质上是同一类型;

泛型类派生子类

  1. 子类也是泛型类,子类和父类的泛型类型要一致;

    class ChildGeneric<T> extends Generic<T>

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

    class ChildGeneric extends Generic<String>

泛型接口

interface 接口名 <泛型标识, ...泛型标识> {
    泛型标识 方法名();
    ......
}
  • 实现类不是泛型类,接口要明确数据类型;
  • 实现类也是泛型类,实现类要和接口的泛型类型一致;

泛型方法

修饰符 <T, E, ...> 返回值类型 方法名(形参列表) {
	方法体...;
}
  • 泛型标识列表 <T, E, …> 非常重要,可以理解为声明此方法为泛型方法;
  • 泛型标识 在调用方法时指定,只在方法体内有效,独立于类存在;
  • 泛型标识 可以在形参列表中使用,在调用时指定;

  • 泛型方法可以声明为 static 静态方法;
  • 泛型方法参数可使用可变参数;
public static <T> void test(T t){}

public static <T> void test(T... t){}

类型通配符

类型通配符一般是使用 ? 代替具体的泛型类型参;

// 这里 ? 可以代表任意具体的类型
public void test(List<?> numberList){}

类型通配符的上限

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

// 示例: 表明该泛型只能为数字类型(上面不指定默认为 Object)
public void test(List<? extends Number> numberList){}

要求该泛型的类型,只能是实参类型,或实参类型的类类型。

类型通配符的下限

/接口<? super 实参类型>{}

// 示例: 表明该泛型只能为Number或Number的父类类型
public void test(List<? super Number> numberList){}

要求该泛型的类型,只能是实参类型,或实参类型的类类型。

类型擦除

泛型是 Java 1.5 版本才引入的概念,在这之前是没有泛型的,但是,泛型代码能够很好的和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入 JVM之前,与泛型相关的信息会被擦除掉,我们称之为——类型擦除。

  • 无限制的类型擦除:会编译为 Object 类型;
  • 有限制的类型擦除:对于有上限的泛型类型,会编译为 上限类型;

桥接方法

对于泛型接口的实现类或泛型类的子类中带泛型类型的方法,类型擦除时会自动生成一个类型为Object的同名方法,称之为桥接方法。

// 定义
public interface Base<T> {
    T process(T t);
}

public class BaseImpl implements Base<String> {
    @Override
    public String process(String val) {
        return val;
    }
}

// 类型擦除
public class BaseImpl implements Base<String> {
    public String process(String val) {
        return val;
    }
    
    // 桥接方法,保持接口与类的实现关系
    @Override
    public Object process(Object val) {
        return process((String)val);
    }
}

由于接口中的泛型标识 T 在类型擦除时会被编译为 Object,所以在实现类中为了保持与接口实现的规范与约束,会自动生成桥接方法完成中间的桥接。

泛型与数组

泛型数组的创建:

  • 可以声明带泛型的数据引用,但是不能直接创建带泛型的数据对象;
  • 可以通过java.lang.reflect.Array.newInstance(Class<T>, int) 创建 T[] 数组;
ArrayList<String>[] listArr;  // correct
ArrayList<String>[] listArr = new ArrayList<String>[5]; // 编译报错 Generic array creation
ArrayList<String>[] listArr = new ArrayList[5]; // correct

// 创建泛型数组,参数:Class 泛型类型,int 数组长度
T[] array = (T[])Array.newInstance(Class<T>, int);

泛型在编译期会做类型擦除的操作,而数组在编译器会一直持有其创建时定义的类型,二者在类型方面是冲突的。

在开发过程中,推荐使用泛型集合来代替泛型数组实现功能

泛型与反射

反射中常用的泛型类:

  • Class<T>
  • Constructor<T>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值