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笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。