首页 > 编程笔记 > Java笔记

Java泛型类的定义和使用(非常详细)

泛型(Generic)是 Java 从 JDK 1.5 开始引入的,可以帮助用户建立类型安全的集合。

泛型的本质就是“数据类型的参数化”,可以把泛型理解为数据类型的一个占位符(形式参数),即在编写代码时将数据类型定义成参数,这些参数在使用或调用时传入具体的类型。

在 JDK 5.0 之前,为了实现参数类型的任意化,都是通过 Object 类型来处理的。但这种处理方式的缺点是需要进行强制类型转换,这种转换不仅会使代码臃肿,还要求开发者必须在已知实际使用的参数类型的情况下才能进行,否则容易引起 ClassCastException 异常。

使用泛型的好处是在程序编译期间会对类型进行检查,捕捉类型不匹配错误,以免引起 ClassCastException 异常,也就不会出现使用 Object 类所带来的问题;在使用了泛型的集合中,遍历时不必进行强制类型转换,因为数据类型都是自动转换的。

泛型经常在类、接口和方法的定义中使用,分别称为泛型类、泛型接口和泛型方法。

泛型类的定义

泛型类是带参数的类,并且有属性和方法。属性的数据类型既可以是已有类型,又可以是“类型参数”的类型。

1) 泛型类的定义形式

定义泛型类的语法格式如下:
[访问符] class 类名<类型参数列表>{
    // 类体......
}
尖括号中是类型参数列表,可以由多个类型参数组成,多个类型参数之间使用“,”隔开。类型参数只是占位符。一般使用大写字母“T”、“U”和“V”等作为类型参数。其他常用的泛型类型参数有以下几个:

2) 泛型类的对象

定义泛型类的对象的语法格式如下:
泛型类名[<实际类型列表>] 对象名 = new 泛型类名[<实际类型列表>]([形参表])
或者:
泛型类名[<实际类型列表>] 对象名 = new 泛型类名[<>]([形参表])
这里的实际类型不能是基本数据类型,必须是类和接口类型。<实际类型列表>也可以不使用,但是泛型类中的所有对象都用 Object 类的对象表示。也可以用“?”代替“实际类型列表”(“?”可以表示任何一个类)。“?”有以下 3 种使用形式:
后两种是有界类型,限制了类型参数的取值范围。下面以泛型通配符“?”的应用为例展开介绍。

【实例 1】泛型通配符“?”的应用。
// 定义泛型类
class Generic<T> {
    private T data;

    public Generic() {}

    public Generic(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public void showDataType() {
        System.out.println("此数据类型是:" + data.getClass().getName());
    }
}

public class Demo {
    // 泛型类Generic的类型参数使用Object
    public static void myMethod(Generic<Object> g) {
        g.showDataType();
    }

    public static void main(String[] args) {
        // 参数类型是Object
        Generic<Object> gobj = new Generic<>("object");
        myMethod(gobj);
        // 参数类型是Integer
        Generic<Integer> gint = new Generic<>(10); // 这里假设传入的整数是10
        // 此处将产生错误,因为myMethod期望的是Generic<Object>类型
        myMethod(gint);
        // 参数类型是Double
        Generic<Double> gdbl = new Generic<>(1.23);
        // 此处也将产生错误,因为myMethod期望的是Generic<Object>类型
        myMethod(gdbl);
    }
}
运行结果显示,有两处错误:

不兼容的类型Generic<java.lang.Integer>无法转换为Generic<java.lang.Object>
不兼容的类型Generic<java.lang.Double>无法转换为Generic<java.lang.Object>

采用泛型通配符“?”可以解决这个问题。读者可以将 Demo 类的 mymethod() 方法的参数类型由 Generic<Object> 修改为 Generic<?>,程序将不会再出现编译错误。这是因为 Generic<?> 相当于 Generic<?extends Object>,用户可以将 Generic<Integer>、Generic<Double> 等传递给 mymethod() 方法。请读者自行修改并运行程序。

下面定义一个泛型类并实例化。

【实例 2】泛型类的应用。
// 定义泛型类
class Generic<T> {
    private T data;

    // 无参构造方法
    public Generic() {}

    // 带参构造方法
    public Generic(T data) {
        this.data = data;
    }

    // 获取数据
    public T getData() {
        return data;
    }

    // 设置数据
    public void setData(T data) {
        this.data = data;
    }

    // 显示数据类型
    public void showDataType() {
        System.out.println("此数据类型是:" + data.getClass().getName());
    }
}

public class Demo {
    public static void main(String[] args) {
        System.out.println("以下使用带参数的泛型构造方法,指定参数的具体类型为String");
        // 指定参数的具体类型为String
        Generic<String> strobj = new Generic<>("泛型类的实例化");
        strobj.showDataType();
        System.out.println(strobj.getData());

        System.out.println("以下为指定参数的具体类型为Double");
        // 指定参数的具体类型为Double,注意构造方法的调用
        Generic<Double> dblobj = new Generic<>(0.123);
        dblobj.showDataType();
        System.out.println(dblobj.getData());

        System.out.println("以下为指定参数的具体类型为Integer");
        // 指定参数的具体类型为Integer,使用无参构造方法,然后设置数据
        Generic<Integer> intobj = new Generic<>();
        intobj.setData(123);
        intobj.showDataType();
        System.out.println(intobj.getData());
    }
}
运行结果为:

以下使用带参数的泛型构造方法,指定参数的具体类型为String
此数据类型是:java.lang.String
泛型类的实例化
以下为指定参数的具体类型为Double
此数据类型是:java.lang.Double
0.123
以下为指定参数的具体类型为Integer
此数据类型是:java.lang.Integer
123

泛型接口的定义

定义泛型接口的语法格式如下:
interface 接口名<类型参数列表>{
}
在实现接口时,也应该声明与接口相同的类型参数:
class 类名<类型参数列表> implements 接口名<类型参数列表>{
}

泛型方法的定义

1) 泛型方法

泛型方法可以定义在泛型类中,也可以定义在非泛型类中。

定义泛型方法的语法格式如下:
[访问修饰符] [static] <类型参数列表> 方法类型 方法名([参数列表]){}

2) 具有可变参数的方法

定义具有可变参数的方法的语法格式如下:
[访问修饰符] <类型参数列表> 方法类型 方法名([类型参数名...参数名]){}
其中,“参数名”从实质上来说是一个数组,当具有可变参数的方法被调用时,其实是将实际参数放到数组中。

【实例 3】具有可变参数的方法的定义与应用。
public class Demo {
    // 泛型方法,形参是可变参数
    public static <T> void show(T... arr) {
        // 访问形参数组中的元素
        for (T t : arr) {
            System.out.print(t + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        // 3个实际参数,类型一样
        show("西红柿", "黄瓜", "茄子");
       
        // 定义字符串数组
        String[] fruit = {"西红柿", "黄瓜", "茄子"};
        // 1个参数,整个数组作为参数传递
        show(fruit);
       
        // 4个实际参数,类型不一样
        show(2024, "年", 8, "月");
    }
}
运行结果如下:

西红柿 黄瓜 茄子
西红柿 黄瓜 茄子
2024 年 8 月


声明:《Java系列教程》为本站“54笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。