java泛型

一,为什么会有泛型

假设我们想要定一个StringArrayList类:

go 复制代码
public class StringArrayList {
    private String[] array;
    private int size;
    public void add(String e) {...}
    public void remove(int index) {...}
    public String get(int index) {...}
}

然后我们又想要定义一个intArrayList类:

go 复制代码
public class IntegerArrayList {
    private Integer[] array;
    private int size;
    public void add(Integer e) {...}
    public void remove(int index) {...}
    public Integer get(int index) {...}
}

这不就重复书写代码了吗?

为了复用代码,我们就想着有一个字符来指代这个类型.于是就有了泛型,顾名思义,就是广义上的类型的别称.

go 复制代码
public class ArrayList<T> {
    private T[] array;
    private int size;
    public void add(T e) {...}
    public void remove(int index) {...}
    public T get(int index) {...}
}

这里ArrayList就是说明这是使用泛型定义的类,其中T就是这个类型代号.

具体使用的时候,把这个T用具体的类型去赋值即可.

go 复制代码
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

二,两种泛型

既然泛型是类型代号,那有一个类型代号,自然也可以有两个类型代号.

go 复制代码
public class Pair<T, K> {
    private T first;
    private K last;
    public Pair(T first, K last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public K getLast() { ... }
}

在使用的时候:

go 复制代码
Pair<String, Integer> p = new Pair<>("test", 123);

三,java泛型的实现方式:擦拭法

擦拭法就是说:虚拟机对泛型其实一无所知,所有的工作都是编译器做的。

当我们书写这样的泛型代码:

go 复制代码
public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
}

而虚拟机根本不知道泛型。这是虚拟机执行的代码:

go 复制代码
public class Pair {
    private Object first;
    private Object last;
    public Pair(Object first, Object last) {
        this.first = first;
        this.last = last;
    }
    public Object getFirst() {
        return first;
    }
    public Object getLast() {
        return last;
    }
}

这就导致了一些泛型的局限性:

3.1,不能是基本类型

例如int,因为实际类型是Object,Object类型无法持有基本类型:

go 复制代码
Pair<int> p = new Pair<>(1, 2); // compile error!

3.2,无法取得带泛型的Class

go 复制代码
public class Main {
    public static void main(String[] args) {
        Pair<String> p1 = new Pair<>("Hello", "world");
        Pair<Integer> p2 = new Pair<>(123, 456);
        Class c1 = p1.getClass();
        Class c2 = p2.getClass();
        System.out.println(c1==c2); // true
        System.out.println(c1==Pair.class); // true

    }
}

class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
}

因为T是Object,我们对Pair和Pair类型获取Class时,获取到的是同一个Class,也就是Pair类的Class。

换句话说,所有泛型实例,无论T的类型是什么,getClass()返回同一个Class实例,因为编译后它们全部都是Pair。

3.3,无法判断带泛型的类型

go 复制代码
Pair<Integer> p = new Pair<>(123, 456);
// Compile error:
if (p instanceof Pair<String>) {
}

原因和前面一样,并不存在Pair.class,而是只有唯一的Pair.class。

四,entends字符

在Java的泛型中,extends关键字用于限定泛型类型参数的上界(Upper Bound),表示该类型参数必须是指定类型或指定类型的子类。这样可以在泛型类或方法中限制传入的类型参数范围,提高代码的类型安全性。

下面是一个简单的示例,演示了如何在泛型中使用extends关键字:

go 复制代码
// 定义一个泛型类,类型参数必须是Number类或其子类
class Box<T extends Number> {
    private T value;

    public Box(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

public class Main {
    public static void main(String[] args) {
        // 实例化一个Box对象,传入Integer类型参数
        Box<Integer> integerBox = new Box<>(10);
        System.out.println("Integer Value: " + integerBox.getValue());

        // 编译错误,String不是Number类或其子类
        // Box<String> stringBox = new Box<>("Hello");
    }
}

在上面的示例中,泛型类Box使用extends Number限定了类型参数T必须是Number类或其子类。因此,可以实例化Box对象,但不能实例化Box对象,因为String不是Number类或其子类。

五,super字符

在Java的泛型中,super关键字用于限定泛型类型参数的下界(Lower Bound),表示该类型参数必须是指定类型或指定类型的父类。使用super关键字可以在泛型类或方法中限制传入的类型参数范围,提高代码的灵活性和可复用性。

下面是一个简单的示例,演示了如何在泛型中使用super关键字:

go 复制代码
// 定义一个泛型类,类型参数必须是Comparable类或其父类
class Box<T super Comparable<T>> {
    private T value;

    public Box(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

public class Main {
    public static void main(String[] args) {
        // 实例化一个Box对象,传入Integer类型参数
        Box<Integer> integerBox = new Box<>(10);
        System.out.println("Integer Value: " + integerBox.getValue());

        // 实例化一个Box对象,传入String类型参数
        Box<String> stringBox = new Box<>("Hello");
        System.out.println("String Value: " + stringBox.getValue());
    }
}

泛型类Box使用super Comparable限定了类型参数T必须是Comparable类或其父类。因此,可以实例化Box和Box对象,因为Integer和String都实现了Comparable接口。

相关推荐
郝学胜-神的一滴1 天前
Linux下,获取子进程退出值和异常终止信号
linux·服务器·开发语言·c++·程序人生
程序定小飞1 天前
基于springboot的学院班级回忆录的设计与实现
java·vue.js·spring boot·后端·spring
AI科技星1 天前
张祥前统一场论动量公式P=m(C-V)误解解答
开发语言·数据结构·人工智能·经验分享·python·线性代数·算法
攀小黑1 天前
基于若依-内容管理动态修改,通过路由字典配置动态管理
java·vue.js·spring boot·前端框架·ruoyi
CodeByV1 天前
【C++】继承
开发语言·c++
青云交1 天前
Java 大视界 -- 基于 Java 的大数据可视化在城市空气质量监测与污染溯源中的应用
java·spark·lstm·可视化·java 大数据·空气质量监测·污染溯源
权泽谦1 天前
用 Python 做一个天气预报桌面小程序(附源码 + 打包与部署指导)
开发语言·python·小程序
ftpeak1 天前
《Rust+Slint:跨平台GUI应用》第八章 窗体
开发语言·ui·rust·slint
森语林溪1 天前
大数据环境搭建从零开始(十七):JDK 17 安装与配置完整指南
java·大数据·开发语言·centos·vmware·软件需求·虚拟机
lsx2024061 天前
HTML 音频(Audio)详解
开发语言