Java进阶篇1-2:泛型

Java泛型完全指南

本篇博客带你从零掌握泛型。内容包括:泛型类、泛型接口、泛型方法、通配符与上下限、包装类等。所有代码均来自 com.wfs.demo2genericity 等包,可直接运行验证。


1 认识泛型

1.1 什么是泛型?

泛型 (Generics)是 JDK 5 引入的特性,允许我们在定义类、接口、方法时使用类型变量 (如 <E><T>),在真正使用时才确定具体的类型。

java 复制代码
public class ArrayList<E> {
    ...
}
  • 泛型类:class 类名<类型变量> { }
  • 泛型接口:interface 接口名<类型变量> { }
  • 泛型方法:修饰符 <类型变量> 返回值 方法名(形参) { }

注意 :类型变量建议用大写的英文字母,常用:E(Element)、T(Type)、K(Key)、V(Value)。

1.2 泛型的作用

  1. 编译阶段约束数据类型:在编译时就确定集合中能放什么类型的数据,防止误操作。
  2. 自动检查,避免强制类型转换 :取出数据时直接得到目标类型,无需手动强转,也就避免了 ClassCastException
  3. 提高代码复用性:一套代码可以处理多种类型。

1.3 泛型的本质

把具体的数据类型作为参数传给类型变量。类似于方法的形式参数,泛型是类型的"形式参数",使用时传入"实际类型参数"。

【案例】GenericDemo1:使用泛型的好处

java 复制代码
package com.wfs.demo2genericity;

import java.util.ArrayList;

public class GenericDemo1 {
    public static void main(String[] args) {
        // 不使用泛型(旧写法):
        // ArrayList list = new ArrayList();
        // list.add("java");
        // list.add(23);      // 不小心混入其他类型
        // String s = (String) list.get(1); // 运行时 ClassCastException

        // 使用泛型:
        ArrayList<String> list = new ArrayList<String>();
        list.add("java");
        list.add("php");
        // list.add(23);      // 编译报错,无法添加非String类型
        // list.add(99.9);

        // 获取数据时无需强转
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);   // 直接得到String
            System.out.println(s);
        }
    }
}

运行结果(仅输出两行):

复制代码
java
php

结论:泛型让集合在编译时就限定了元素类型,避免了类型转换异常。


2 泛型类

2.1 定义语法

java 复制代码
修饰符 class 类名<类型变量, 类型变量,...> {
    // 可以使用类型变量定义字段、方法参数、返回值
}

2.2 自定义泛型类示例:模拟 ArrayList

【案例】MyArrayList(自定义泛型类)

java 复制代码
// MyArrayList.java
package com.wfs.demo2genericity;

import java.util.ArrayList;

public class MyArrayList<E> {
    private ArrayList list = new ArrayList();

    public boolean add(E e) {
        list.add(e);
        return true;
    }

    public boolean remove(E e) {
        return list.remove(e);
    }

    @Override
    public String toString() {
        return list.toString();
    }
}

【使用】GenericDemo2

java 复制代码
package com.wfs.demo2genericity;

public class GenericDemo2 {
    public static void main(String[] args) {
        // 泛型擦除:编译后泛型信息被擦除,运行时无泛型
        MyArrayList<String> mlist = new MyArrayList<>(); // JDK 7钻石语法
        mlist.add("hello");
        mlist.add("world");
        // mlist.add(555); // 编译报错
        mlist.add("java");
        mlist.add("前端");

        System.out.println(mlist.remove("world")); // true
        System.out.println(mlist);                 // [hello, java, 前端]
    }
}

运行结果

复制代码
true
[hello, java, 前端]

解释MyArrayList<E>E 在使用时被指定为 String,编译器会约束所有添加的元素必须是 String,取出时也是 String(实际是 Object,但编译器做了类型安全保证)。


3 泛型接口

3.1 定义语法

java 复制代码
修饰符 interface 接口名<类型变量, 类型变量,...> {
    // 抽象方法可以使用类型变量
}

3.2 实现方式

实现类可以在实现接口时指定具体类型 ,也可以继续保留泛型

【案例】泛型接口 Data

java 复制代码
// Data.java
package com.wfs.demo3genericity;

public interface Data<T> {
    void add(T t);
    void delete(T t);
    void update(T t);
    T query(int id);
}

【实现类】StudentData 和 TeacherData

java 复制代码
// StudentData.java
public class StudentData implements Data<Student> {
    @Override
    public void add(Student student) { /* 实现代码 */ }
    @Override
    public void delete(Student student) { }
    @Override
    public void update(Student student) { }
    @Override
    public Student query(int id) { return new Student(); }
}

// TeacherData.java
public class TeacherData implements Data<Teacher> {
    // 类似实现 ...
}

【使用】GenericDemo3

java 复制代码
public class GenericDemo3 {
    public static void main(String[] args) {
        StudentData studentData = new StudentData();
        studentData.add(new Student());
        studentData.delete(new Student());
        Student s = studentData.query(1);
    }
}

说明Data<T> 接口通过泛型,让 StudentDataTeacherData 复用同一套接口定义,分别操作 StudentTeacher 对象,避免编写重复接口。


4 泛型方法

4.1 定义语法

java 复制代码
修饰符 <类型变量, 类型变量,...> 返回值类型 方法名(形参列表) {
    // 方法体内可以使用类型变量
}
  • 注意 :泛型方法的类型变量放在返回值之前,且只在当前方法中有效。

【案例】GenericDemo4:泛型方法示例

java 复制代码
package com.wfs.demo4genericity;

public class GenericDemo4 {
    public static void main(String[] args) {
        String[] names = {"赵敏", "张无忌", "周芷若", "小昭"};
        printArray(names);              // 自动推断 T 为 String

        Student[] stus = new Student[3];
        printArray(stus);               // T 为 Student

        Student max = getMax(stus);     // 返回 Student
        String max2 = getMax(names);    // 返回 String
    }

    // 泛型方法:打印任意类型的数组
    public static <T> void printArray(T[] arr) {
        for (T t : arr) {
            System.out.print(t + " ");
        }
        System.out.println();
    }

    // 泛型方法:获取数组中的最大值(假设有比较逻辑)
    public static <T> T getMax(T[] arr) {
        // 实际需要 T 实现 Comparable,这里仅示意
        return arr.length > 0 ? arr[0] : null;
    }
}

作用 :同一个方法可以处理 String[]Student[] 等多种类型,无需重载。


5 通配符与上下限

5.1 通配符 ?

  • 通配符 ? 表示"未知的类型",在使用泛型时(而不是定义)代表一切类型。
  • 定义时用 T,使用时用 ?

5.2 泛型上限与下限

  • 上限? extends Car → 能接收 Car 或其子类。
  • 下限? super Car → 能接收 Car 或其父类。

【案例】GenericDemo5:极品飞车游戏(通配符 + 上限)

java 复制代码
package com.wfs.demo4genericity;

import java.util.ArrayList;

public class GenericDemo5 {
    public static void main(String[] args) {
        ArrayList<Xiaomi> xiaomis = new ArrayList<>();
        xiaomis.add(new Xiaomi());
        go(xiaomis);   // 可以传递 Xiaomi 集合

        ArrayList<BYD> byds = new ArrayList<>();
        byds.add(new BYD());
        go(byds);      // 可以传递 BYD 集合

        // ArrayList<Dog> dogs = new ArrayList<>();
        // go(dogs);   // 编译报错,因为 Dog 不是 Car 的子类
    }

    // 使用通配符上限:只允许 Car 及其子类的集合
    public static void go(ArrayList<? extends Car> cars) {
        // 只能遍历,不能添加元素(因为不知道具体子类型)
        for (Car c : cars) {
            System.out.println(c);
        }
    }
}

解释 :虽然 XiaomiBYD 都是 Car 的子类,但 ArrayList<Xiaomi>ArrayList<BYD>ArrayList<Car> 没有继承关系。通配符 ? extends Car 表示"任何 Car 的子类类型的 ArrayList",从而实现了参数的多态。

注意 :使用 ? extends Car 时,不能向集合中添加元素(除了 null),因为无法确定具体的子类型。


6 泛型与包装类

6.1 为什么需要包装类?

泛型不支持基本数据类型,只能支持引用数据类型(对象类型) 。因为泛型在编译后被擦除为 Object,而基本类型不是 Object 的子类。所以我们需要包装类。

基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
char Character
float Float
double Double
boolean Boolean

6.2 自动装箱与自动拆箱

  • 自动装箱:基本类型自动转换为包装类对象。
  • 自动拆箱:包装类对象自动转换为基本类型。

【案例】GenericDemo6:包装类与自动装箱拆箱

java 复制代码
package com.wfs.demo5genericity;

import java.util.ArrayList;

public class GenericDemo6 {
    public static void main(String[] args) {
        // 泛型不支持基本类型,必须使用包装类
        // ArrayList<int> list = new ArrayList<>(); // 编译错误
        ArrayList<Integer> list = new ArrayList<>();
        list.add(130);   // 自动装箱:int -> Integer
        list.add(120);
        int rs = list.get(1); // 自动拆箱:Integer -> int
        System.out.println(rs); // 120

        // 手工包装
        Integer it1 = Integer.valueOf(100);
        Integer it2 = Integer.valueOf(100);
        System.out.println(it1 == it2); // true,因为 -128~127 有缓存

        Integer it11 = 130;
        Integer it22 = 130;
        System.out.println(it11 == it22); // false,超过缓存范围,不同对象

        // 自动拆箱
        int i = it11; // Integer 自动转为 int
        System.out.println(i); // 130
    }
}

6.3 包装类的常用功能

  1. 基本类型 → 字符串
java 复制代码
int j = 23;
String rs1 = Integer.toString(j);   // "23"
String rs3 = j + "";                // 推荐简单方式
  1. 字符串 → 基本类型(常用)
java 复制代码
String str = "98";
int i1 = Integer.parseInt(str);   // 98
int i2 = Integer.valueOf(str);    // 推荐,同样返回 int
double d = Double.parseDouble("98.8");

【案例】完整演示

java 复制代码
// 包装类新增功能
public static void main(String[] args) {
    // 1. 基本类型 -> 字符串
    int j = 23;
    String rs1 = Integer.toString(j);
    System.out.println(rs1 + 1); // 231

    // 2. 字符串 -> 基本类型
    String str = "98";
    int i1 = Integer.parseInt(str);
    System.out.println(i1 + 2); // 100

    String str2 = "98.8";
    double d = Double.parseDouble(str2);
    System.out.println(d + 2); // 100.8
}

7 总结

知识点 要点
泛型本质 类型参数化,编译时检查,运行时擦除
泛型类 class MyArrayList<E>,使用时指定具体类型
泛型接口 interface Data<T>,实现时可确定类型
泛型方法 public static <T> void test(T t),独立于类的泛型
通配符 ? 用于使用泛型时,? extends Car 上限,? super Car 下限
包装类 8种基本类型对应的引用类型,解决泛型不支持基本类型问题
自动装箱/拆箱 基本类型与包装类之间自动转换
包装类常用功能 parseIntvalueOftoString

一句话:泛型让代码更安全、更通用;包装类让基本类型也能"万物皆对象"。

本文案例代码均位于 com.wfs.demo2genericity ~ demo5genericity 包,可直接运行体验。下一节我们将学习集合框架,敬请期待!

相关推荐
码语智行1 小时前
Codex 新手安装教程(完全小白版)
java·人工智能
say_fall1 小时前
模拟量输入输出技术超详细知识点总结
linux·开发语言·嵌入式硬件·学习·php
我是一颗柠檬1 小时前
C++最全面复习:从入门到精通(2026年)
开发语言·c++·visualstudio
xingpanvip1 小时前
使用 Webwright 在 CSDN 自动发文:Python 浏览器自动化实践
开发语言·python·自动化
禅思院1 小时前
大列表性能优化 · 工程实战·四
开发语言·前端·性能优化·前端框架·php·异步加载
z落落1 小时前
C# 多接口实现、重名成员、显式实现、接口继承+抽象类和接口区别
java·开发语言·c#
caimouse1 小时前
Reactos 第 4 章 对象管理 — 4.6 对象的访问控制 / 4.7 句柄的遗传和继承
开发语言·windows·架构
C137的本贾尼1 小时前
【实战】分析一张真实业务表的 InnoDB 存储结构
java·大数据·数据库
huangdong_1 小时前
京东整店商品图片视频批量下载技术:从商品列表到自动分类
开发语言·python·音视频