概述
作为一个面向物件的编程语言,Java可以通过实作一些类,作为我们各种需求的一个模板,方便我们的使用,但有时候,这个类的范围可能比我们想要的范围要大,我们只想限定于满足类的某些物件,那这样的情况下,泛型的概念就被提出来了(非官方解释,方便理解),
举个例子:比如我们我们生活中的车,它可以作为一个类,但是车其实又有很多种,包括货车,轿车,大巴车等等,而其中的轿车外观差不多,但是又属于不同的品牌,这些品牌有很多不一样的地方,这里我们可以把轿车的品牌看作是泛型(类似于标签)
通过上面的解释,泛型的概念就比较清晰了,就是一种“型别自变量”,所谓型别自变量可以理解为将型别由原来的具体的型别进行自变量化,类似于方法中的变量自变量,此时型别也定义成自变量形式(可以称之为型别形参),然后在使用/呼叫时传入具体的型别(型别实参),
泛型的优点,不仅仅是上面提到的,其还有下面的优点::
- 型别安全: 提高Java 程序的型别安全(泛型的主要目标),
通过知道使用泛型定义的变量的型别限制,编译器可以验证型别假设, - 消除强制型别转换:消除源代码中的许多强制型别转换,
这使得代码的可读性更高了,并且还减少了错误
上面说到了泛型在类中的使用,其实泛型的使用远不止于此,其还可以在在界面、方法中使用,下面就对这些分别进行介绍
泛型类
所谓泛型类就是把当我们在宣告类时,类中的有些成员的型别并不是确定,然后我们可以把泛型定义在类上,当使用该类的时候,再把不确定成员的型别明确下来,
语法格式
【修饰符】 class 类名<型别变量串列>{
//类体
}
注:指定泛型实参时,必须左右两边一致,不存在多型现象(右边的可以省略不写)
代码示例
/*
泛型类的宣告与使用
*/
public class Demo1 {
public static void main(String[] args) {
//泛型类的使用(<T>里面只能是参考型别)
Student<Double> student1 = new Student<>("学生1",99.5);
Student<String> student2 = new Student<>("学生2","优秀");
Student<Character> student3 = new Student<>("学生3",'A');
//输出结果
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);
}
}
//泛型类的宣告
class Student<T> { //<T>这个就是泛型类的型别自变量
private String name;
private T score; //使用泛型,定义分数(分数可能有double型别(99.5)、字符串型别(优秀)、字符型别(‘A’)等)
//构造方法
public Student() {
}
public Student(String name, T score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
泛型界面
泛型界面和泛型类关系,就像界面和类的关系一样, 这里不多说,
语法格式
【修饰符】 interface 界面名<型别变量串列>{
}
注:如果多个上限中有类有界面,那么只能有一个类,而且必须写在最左边,界面的话,可以多个,
如果在宣告<型别变量>时没有指定上限,默认上限是java.lang.Object,
代码示例
/*
型别变量的上限
*/
public class Demo2 {
public static void main(String[] args) {
Test<Double> test1 = new Test<>(77.5); //double类
// Test<String> test2 = new Test<String>(); 不是数字类的子类
Test<Integer> test3 = new Test<>(18);
test1.print(77.5);
test3.print(18);
}
}
class Test<T extends Number >{ //数字类上限,只能使用数字类及其子类
private T num;
public Test() {
}
public Test(T num) {
this.num = num;
}
public void print(T num){ //测验方法
System.out.println(num);
}
}
型别变量的下限
如果泛型类定义了型别变量的下限,那么该泛型类实际的型别只能是该下限型别或者其父型别别,
语法格式
<? super E > // ? 代表接收E型别或者E的父型别的元素
注:前面说到类和界面上的型别形参是不能用于静态方法
语法格式
【修饰符】 <型别变量串列> 回传值型别 方法名(【形参串列】)【throws 例外串列】{
//方法体
}
注:- <型别变量串列>:可以是一个或多个型别变量,一般都是使用单个的大写字母表示,例如: < T >、<K,V>等,
<型别变量>同样也可以指定上限
代码示例
/*
泛型方法
*/
public class Demo3 {
public static void main(String[] args) {
Test1 test = new Test1(); //创建测验物件
test.print(12); //测验
test.print(12.5); //测验
}
}
class Test1{
public <T extends Number> void print(T t){ //泛型方法,可以设定上限
System.out.println("这是一个泛型方法,测验型别:" + t);
}
}
泛型擦除
泛型擦除只是在编译阶段才会有的,在实际运行阶段型别已经确定了,这个时候就没有泛型的概念了(JVM并不知道泛型的存在),这个从有泛型信息到没有泛型信息的程序称之为“泛型擦除”,
其擦除规则如下:
- 若泛型型别没有指定具体型别,用Object作为原始型别;
- 若有限定型别< T exnteds XClass >,使用XClass作为原始型别;
- 若有多个限定< T exnteds XClass1 & XClass2 >,使用第一个边界型别XClass1作为原始型别;
型别通配符
通配符的意思是可以指代很多型别,这个主要使用在当我们在宣告方法时,不确定该泛型实际型别的情况,型别通配符有三种:
- <?> 任意型别
- <? extends 上限>
- <? super E>
下面对这三种通配符分别进行介绍
<?> 任意型别
当泛型使用这种 型别通配符的时候,表示可以使用任意型别
代码示例
/*
型别通配符
*/
public class Demo4 {
public static void main(String[] args) {
// 语文老师使用时:
StudentInfo<String> stu1 = new StudentInfo<String>("张三", "良好");
// 数学老师使用时:
StudentInfo<Double> stu2 = new StudentInfo<Double>("张三", 90.5);
// 英语老师使用时:
StudentInfo<Character> stu3 = new StudentInfo<Character>("张三", 'C');
StudentInfo<?>[] arr = new StudentInfo[3]; //使用通配符
arr[0] = stu1;
arr[1] = stu2;
arr[2] = stu3;
StudentInfoPrint.print(arr); //打印输出结果
}
}
//学生类是一个自变量化的泛型类
class StudentInfo<T>{
private String name;
private T score;
public StudentInfo() {
super();
}
public StudentInfo(String name, T score) {
super();
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "姓名:" + name + ", 成绩:" + score;
}
}
//学生信息打印类
class StudentInfoPrint {
//泛型方法,使用通配符
public static void print(StudentInfo<?>[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
<? extends 上限>
? 代表接收E型别或者E的子型别的元素
代码示例
可参考上面的型别变量的上限代码
<? super E>
? 代表接收E型别或者E的父型别的元素
代码示例
可参考上面的型别变量的下限代码
0 评论