Java泛型类的定义和使用(非常详细)
泛型(Generic)是 Java 从 JDK 1.5 开始引入的,可以帮助用户建立类型安全的集合。
泛型的本质就是“数据类型的参数化”,可以把泛型理解为数据类型的一个占位符(形式参数),即在编写代码时将数据类型定义成参数,这些参数在使用或调用时传入具体的类型。
在 JDK 5.0 之前,为了实现参数类型的任意化,都是通过 Object 类型来处理的。但这种处理方式的缺点是需要进行强制类型转换,这种转换不仅会使代码臃肿,还要求开发者必须在已知实际使用的参数类型的情况下才能进行,否则容易引起 ClassCastException 异常。
使用泛型的好处是在程序编译期间会对类型进行检查,捕捉类型不匹配错误,以免引起 ClassCastException 异常,也就不会出现使用 Object 类所带来的问题;在使用了泛型的集合中,遍历时不必进行强制类型转换,因为数据类型都是自动转换的。
泛型经常在类、接口和方法的定义中使用,分别称为泛型类、泛型接口和泛型方法。
后两种是有界类型,限制了类型参数的取值范围。下面以泛型通配符“?”的应用为例展开介绍。
【实例 1】泛型通配符“?”的应用。
下面定义一个泛型类并实例化。
【实例 2】泛型类的应用。
定义泛型方法的语法格式如下:
【实例 3】具有可变参数的方法的定义与应用。
声明:《Java系列教程》为本站“54笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。
泛型的本质就是“数据类型的参数化”,可以把泛型理解为数据类型的一个占位符(形式参数),即在编写代码时将数据类型定义成参数,这些参数在使用或调用时传入具体的类型。
在 JDK 5.0 之前,为了实现参数类型的任意化,都是通过 Object 类型来处理的。但这种处理方式的缺点是需要进行强制类型转换,这种转换不仅会使代码臃肿,还要求开发者必须在已知实际使用的参数类型的情况下才能进行,否则容易引起 ClassCastException 异常。
使用泛型的好处是在程序编译期间会对类型进行检查,捕捉类型不匹配错误,以免引起 ClassCastException 异常,也就不会出现使用 Object 类所带来的问题;在使用了泛型的集合中,遍历时不必进行强制类型转换,因为数据类型都是自动转换的。
泛型经常在类、接口和方法的定义中使用,分别称为泛型类、泛型接口和泛型方法。
泛型类的定义
泛型类是带参数的类,并且有属性和方法。属性的数据类型既可以是已有类型,又可以是“类型参数”的类型。1) 泛型类的定义形式
定义泛型类的语法格式如下:[访问符] class 类名<类型参数列表>{ // 类体...... }尖括号中是类型参数列表,可以由多个类型参数组成,多个类型参数之间使用“,”隔开。类型参数只是占位符。一般使用大写字母“T”、“U”和“V”等作为类型参数。其他常用的泛型类型参数有以下几个:
- T:代表一个类型 Type;
- E:代表一个元素 Element 或一个异常 Exception;
- K:代表一个键 Key;
- V:代表一个值 Value。
2) 泛型类的对象
定义泛型类的对象的语法格式如下:泛型类名[<实际类型列表>] 对象名 = new 泛型类名[<实际类型列表>]([形参表])或者:
泛型类名[<实际类型列表>] 对象名 = new 泛型类名[<>]([形参表])这里的实际类型不能是基本数据类型,必须是类和接口类型。<实际类型列表>也可以不使用,但是泛型类中的所有对象都用 Object 类的对象表示。也可以用“?”代替“实际类型列表”(“?”可以表示任何一个类)。“?”有以下 3 种使用形式:
- ?:不受限制的类型,相当于?extends Object。
- ?extends T:类型参数必须是 T 或 T 的子类。
- ?super T:类型参数必须是 T 或 T 的父类。
后两种是有界类型,限制了类型参数的取值范围。下面以泛型通配符“?”的应用为例展开介绍。
【实例 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>
下面定义一个泛型类并实例化。
【实例 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笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。