基础复习第二十二天 泛型的使用
创始人
2024-05-27 07:18:31
0

泛型

JDK1.5设计了泛型的概念。泛型即为“类型参数”,这个类型参数在声明它的类、接口或方法中,代表未知的通用的类型。例如:

java.lang.Comparable接口和java.util.Comparator接口,是用于对象比较大小的规范接口,这两个接口只是限定了当一个对象大于另一个对象时返回正整数,小于返回负整数,等于返回0。但是并不确定是什么类型的对象比较大小,之前的时候只能用Object类型表示,使用时既麻烦又不安全,因此JDK1.5就给它们增加了泛型。

public interface Comparable{int compareTo(T o) ;
}
public interface Comparator{int compare(T o1, T o2) ;
}

其中就是类型参数,即泛型。

泛型的好处

那么我们在使用如上面这样的接口时,如果没有泛型或不指定泛型,很麻烦,而且有安全隐患。如果有了泛型并使用泛型,那么既能保证安全,又能简化代码。

JavaBean:圆类型

class Circle{private double radius;public Circle(double radius) {super();this.radius = radius;}public double getRadius() {return radius;}public void setRadius(double radius) {this.radius = radius;}@Overridepublic String toString() {return "Circle [radius=" + radius + "]";}}

比较器

import java.util.Comparator;public class CircleComparator implements Comparator{@Overridepublic int compare(Object o1, Object o2) {//强制类型转换Circle c1 = (Circle) o1;Circle c2 = (Circle) o2;return Double.compare(c1.getRadius(), c2.getRadius());}}

测试类

public class TestGeneric {public static void main(String[] args) {CircleComparator com = new CircleComparator();System.out.println(com.compare(new Circle(1), new Circle(2)));System.out.println(com.compare("圆1", "圆2"));//运行时异常:ClassCastException}
}

使用泛型:

比较器:

class CircleComparator implements Comparator{@Overridepublic int compare(Circle o1, Circle o2) {//不再需要强制类型转换,代码更简洁return Double.compare(o1.getRadius(), o2.getRadius());}}

测试类

import java.util.Comparator;public class TestGeneric {public static void main(String[] args) {CircleComparator com = new CircleComparator();System.out.println(com.compare(new Circle(1), new Circle(2)));//        System.out.println(com.compare("圆1", "圆2"));//编译错误,因为"圆1", "圆2"不是Circle类型,编译器提前报错,而不是冒着风险在运行时再报错}
}

其中:是类型变量(Type Variables),Comparator这种就称为参数化类型(Parameterized Types),Comparator中的是参数化类型的类型参数

类比方法的参数,我们可以把,称为类型形参,将称为类型实参,有助于我们理解泛型。 

参数类型:泛型类与泛型接口

当我们在声明类或接口时,类或接口中定义某个成员时,该成员有些类型是不确定的,而这个类型需要在使用这个类或接口时才可以确定,那么我们可以使用泛型。

声明泛型类与泛型接口

语法格式:

【修饰符】 class 类名<类型变量列表>{}
【修饰符】 interface 接口名<类型变量列表>{}
<类型变量列表>:可以是一个或多个类型变量,一般都是使用单个的大写字母表示。例如:等。
<类型变量列表>中的类型变量不能用于静态成员上。

示例代码:

例如:我们要声明一个学生类,该学生包含姓名、成绩,而此时学生的成绩类型不确定,为什么呢,因为,语文老师希望成绩是“优秀”、“良好”、“及格”、“不及格”,数学老师希望成绩是89.5, 65.0,英语老师希望成绩是'A','B','C','D','E'。那么我们在设计这个学生类时,就可以使用泛型。

public class Student{private String name;private T score;public Student() {super();}public Student(String name, T score) {super();this.name = name;this.score = score;}public String getName() {return name;}public void setName(String name) {this.name = name;}public T getScore() {return score;}public void setScore(T score) {this.score = score;}@Overridepublic String toString() {return "姓名:" + name + ", 成绩:" + score;}
}
使用泛型类与泛型接口

在使用这种参数化的类与接口时,我们需要指定泛型变量的实际类型参数:

(1)实际类型参数必须是引用数据类型,不能是基本数据类型

(2)在创建类的对象时指定类型变量对应的实际类型参数

public class TestGeneric{public static void main(String[] args) {//语文老师使用时:Student stu1 = new Student("张三", "良好");//数学老师使用时://Student stu2 = new Student("张三", 90.5);//错误,必须是引用数据类型Student stu2 = new Student("张三", 90.5);//英语老师使用时:Student stu3 = new Student("张三", 'C');//错误的指定//Student stu = new Student();//错误的}
}
JDK1.7支持简写形式:Student stu1 = new Student<>("张三", "良好");
指定泛型实参时,必须左右两边一致,不存在多态现象

(3)在继承泛型类或实现泛型接口时,指定类型变量对应的实际类型参数

class ChineseStudent extends Student{public ChineseStudent() {super();}public ChineseStudent(String name, String score) {super(name, score);}}
public class TestGeneric{public static void main(String[] args) {//语文老师使用时:ChineseStudent stu = new ChineseStudent("张三", "良好");}
}
class Circle implements Comparable{private double radius;public Circle(double radius) {super();this.radius = radius;}public double getRadius() {return radius;}public void setRadius(double radius) {this.radius = radius;}@Overridepublic String toString() {return "Circle [radius=" + radius + "]";}@Overridepublic int compareTo(Circle c){return Double.compare(radius,c.radius);}}
类型变量的上限

当在声明类型变量时,如果不希望这个类型变量代表任意引用数据类型,而是某个系列的引用数据类型,那么可以设定类型变量的上限。

语法格式:

<类型变量  extends 上限>

如果有多个上限

<类型变量  extends 上限1 & 上限2>
如果多个上限中有类有接口,那么只能有一个类,而且必须写在最左边。接口的话,可以多个。
如果在声明<类型变量>时没有指定任何上限,默认上限是java.lang.Object。

例如:我们要声明一个两个数求和的工具类,要求两个加数必须是Number数字类型,并且实现Comparable接口。

class SumTools>{private T a;private T b;public SumTools(T a, T b) {super();this.a = a;this.b = b;}@SuppressWarnings("unchecked")public T getSum(){if(a instanceof BigInteger){return (T) ((BigInteger) a).add((BigInteger)b);}else if(a instanceof BigDecimal){return (T) ((BigDecimal) a).add((BigDecimal)b);}else if(a instanceof Short){return (T)(Integer.valueOf((Short)a+(Short)b));}else if(a instanceof Integer){return (T)(Integer.valueOf((Integer)a+(Integer)b));}else if(a instanceof Long){return (T)(Long.valueOf((Long)a+(Long)b));}else if(a instanceof Float){return (T)(Float.valueOf((Float)a+(Float)b));}else if(a instanceof Double){return (T)(Double.valueOf((Double)a+(Double)b));}throw new UnsupportedOperationException("不支持该操作");}
}

测试类

    public static void main(String[] args) {SumTools s = new SumTools(1,2);Integer sum = s.getSum();System.out.println(sum);//        SumTools s = new SumTools("1","2");//错误,因为String类型不是extends Number}
泛型擦除

当使用参数化类型的类或接口时,如果没有指定泛型,那么会怎么样呢?

会发生泛型擦除,自动按照最左边的第一个上限处理。如果没有指定上限,上限即为Object。

    public static void main(String[] args) {SumTools s = new SumTools(1,2);Number sum = s.getSum();System.out.println(sum);}
import java.util.Comparator;public class CircleComparator implements Comparator{@Overridepublic int compare(Object o1, Object o2) {//强制类型转换Circle c1 = (Circle) o1;Circle c2 = (Circle) o2;return Double.compare(c1.getRadius(), c2.getRadius());}}
public class TestExer1 {public static void main(String[] args) {Coordinate c1 = new Coordinate<>("北纬38.6", "东经36.8");System.out.println(c1);//        Coordinate c2 = new Coordinate<>(38.6, 38);//自动装箱与拆箱只能与对应的类型 38是int,自动装为IntegerCoordinate c2 = new Coordinate<>(38.6, 36.8);System.out.println(c2);}
}
class Coordinate{private T x;private T y;public Coordinate(T x, T y) {super();this.x = x;this.y = y;}public Coordinate() {super();}public T getX() {return x;}public void setX(T x) {this.x = x;}public T getY() {return y;}public void setY(T y) {this.y = y;}@Overridepublic String toString() {return "Coordinate [x=" + x + ", y=" + y + "]";}}

泛型方法

前面介绍了在定义类、接口时可以声明<类型变量>,在该类的方法和属性定义、接口的方法定义中,这些<类型变量>可被当成普通类型来用。但是,在另外一些情况下,

(1)如果我们定义类、接口时没有使用<类型变量>,但是某个方法定义时,想要自己定义<类型变量>;

(2)另外我们之前说类和接口上的类型形参是不能用于静态方法中,那么当某个静态方法想要定义<类型变量>。

那么,JDK1.5之后,还提供了泛型方法的支持。

语法格式:

【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{//...
}
<类型变量列表>:可以是一个或多个类型变量,一般都是使用单个的大写字母表示。例如:等。
<类型变量>同样也可以指定上限

示例代码:

我们编写一个数组工具类,包含可以给任意对象数组进行从小到大排序,要求数组元素类型必须实现Comparable接口

public class MyArrays{public static > void sort(T[] arr){for (int i = 1; i < arr.length; i++) {for (int j = 0; j < arr.length-i; j++) {if(arr[j].compareTo(arr[j+1])>0){T temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}}
}

测试类

public class TestGeneric{public static void main(String[] args) {int[] arr = {3,2,5,1,4};
//        MyArrays.sort(arr);//错误的,因为int[]不是对象数组String[] strings = {"hello","java","chai"};MyArrays.sort(strings);System.out.println(Arrays.toString(strings));Circle[] circles = {new Circle(2.0),new Circle(1.2),new Circle(3.0)};MyArrays.sort(circles);System.out.println(Arrays.toString(circles));}
}

类型通配符

当我们声明一个方法时,某个形参的类型是一个参数化的泛型类或泛型接口类型,但是在声明方法时,又不确定该泛型实际类型,我们可以考虑使用类型通配符。

任意类型

例如:我们要声明一个学生管理类,这个管理类要包含一个方法,可以遍历学生数组。

学生管理类:

class StudentService {public static void print(Student[] arr) {for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}}
}

测试类

public class TestGeneric {public static void main(String[] args) {// 语文老师使用时:Student stu1 = new Student("张三", "良好");// 数学老师使用时:// Student stu2 = new Student("张三", 90.5);//错误,必须是引用数据类型Student stu2 = new Student("张三", 90.5);// 英语老师使用时:Student stu3 = new Student("张三", 'C');Student[] arr = new Student[3];arr[0] = stu1;arr[1] = stu2;arr[2] = stu3;StudentService.print(arr);}
}

例如:我们要声明一个学生管理类,这个管理类要包含一个方法,找出学生数组中成绩最高的学生对象。

要求学生的成绩的类型必须可比较大小,实现Comparable接口。

学生管理类:

class StudentService {@SuppressWarnings({ "rawtypes", "unchecked" })public static Student max(Student[] arr){Student max = arr[0];for (int i = 0; i < arr.length; i++) {if(arr[i].getScore().compareTo(max.getScore())>0){max = arr[i];}}return max;}
}

测试类

public class TestGeneric {@SuppressWarnings({ "rawtypes", "unchecked" })public static void main(String[] args) {Student[] arr = new Student[3];arr[0] = new Student("张三", 90.5);arr[1] = new Student("李四", 80.5);arr[2] = new Student("王五", 94.5);Student max = StudentService.max(arr);System.out.println(max);}
}

现在要声明一个数组工具类,包含可以给任意对象数组进行从小到大排序,只要你指定定制比较器对象,而且这个定制比较器对象可以是当前数组元素类型自己或其父类的定制比较器对象

数组工具类:

class MyArrays{public static  void sort(T[] arr, Comparator c){for (int i = 1; i < arr.length; i++) {for (int j = 0; j < arr.length-i; j++) {if(c.compare(arr[j], arr[j+1])>0){T temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}}
}

例如:有如下JavaBean

class Person{private String name;private int age;public Person(String name, int age) {super();this.name = name;this.age = age;}public Person() {super();}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "name=" + name + ", age=" + age;}
}
class Student extends Person{private int score;public Student(String name, int age, int score) {super(name, age);this.score = score;}public Student() {super();}public int getScore() {return score;}public void setScore(int score) {this.score = score;}@Overridepublic String toString() {return super.toString() + ",score=" + score;}}

测试类

public class TestGeneric {public static void main(String[] args) {Student[] all = new Student[3];all[0] = new Student("张三", 23, 89);all[1] = new Student("李四", 22, 99);all[2] = new Student("王五", 25, 67);MyArrays.sort(all, new Comparator() {@Overridepublic int compare(Person o1, Person o2) {return o1.getAge() - o2.getAge();}});System.out.println(Arrays.toString(all));MyArrays.sort(all, new Comparator() {@Overridepublic int compare(Student o1, Student o2) {return o1.getScore() - o2.getScore();}});System.out.println(Arrays.toString(all));}
}

使用类型通配符来指定类型参数的问题

:不可变,因为类型不确定,编译时,任意类型都是错

:不可变,因为的?可能是上限或上限的子类,即类型不确定,编译按任意类型处理都是错。

:可以将值修改为下限或下限子类的对象,因为?代表是下限或下限的父类,那么设置为下限或下限子类的对象是安全的。

public class TestGeneric {public static void main(String[] args) {Student stu1 = new Student<>();stu1.setScore(null);//除了null,无法设置为其他值Student stu2 = new Student<>();stu2.setScore(null);//除了null,无法设置为其他值Student stu3 = new Student<>();stu3.setScore(56);//可以设置Number或其子类的对象}
}
class Student{private String name;private T score;public Student() {super();}public Student(String name, T score) {super();this.name = name;this.score = score;}public String getName() {return name;}public void setName(String name) {this.name = name;}public T getScore() {return score;}public void setScore(T score) {this.score = score;}@Overridepublic String toString() {return "姓名:" + name + ", 成绩:" + score;}
}

相关内容

热门资讯

安卓系统的经典铃声,唤醒回忆的... 你有没有发现,手机里那些熟悉的铃声,有时候就像老朋友一样,陪伴着我们度过了无数个日日夜夜?今天,就让...
鸿蒙系统还是安卓系统号,系统之... 你有没有想过,手机里的操作系统就像是我们的大脑,它决定了我们手机能做什么,不能做什么。现在,就让我们...
安卓系统装贝达,安卓系统下的贝... 你有没有想过,你的安卓手机装上贝达系统后,会有怎样的奇妙体验呢?想象你的手机瞬间变身,变得流畅无比,...
安卓系统沃尔沃音响设置,轻松享... 你有没有发现,自从你的安卓手机和沃尔沃音响完美结合后,开车时的音乐体验简直就像是在音乐厅里一样?没错...
米10系统基于安卓,基于安卓的... 你知道吗?最近手机圈里可是热闹非凡呢!小米10这款手机,自从发布以来就吸引了无数人的目光。而它所搭载...
命令安卓系统怎么卸载,安卓系统... 手机里装了太多不用的应用,是不是感觉手机都快要爆炸了?别急,今天就来教你怎么轻松卸载安卓系统中的应用...
安卓系统安装小学教材,安卓系统... 你有没有想过,手机里的安卓系统竟然能装上小学教材呢?没错,你没听错!在这个信息爆炸的时代,科技的发展...
华为安卓系统锁住了,揭秘锁屏背... 最近是不是发现你的华为手机有点儿“顽皮”了?它突然间变得神秘起来,屏幕上那个熟悉的安卓系统仿佛被施了...
安卓电脑改苹果系统,跨越平台的... 你有没有想过,把你的安卓电脑改头换面,变成一个优雅的苹果系统使用者呢?想象那流畅的界面,那独特的触控...
安卓系统怎么按后台,并在任务完... 你有没有遇到过这种情况:手机屏幕一黑,安卓系统就自动进入后台了?是不是觉得有点小郁闷,想要手动切换回...
2021年安卓系统ui,202... 你有没有发现,手机界面最近好像换了个模样?没错,2021年的安卓系统UI可是来了一场大变身呢!今天,...
安卓系统程序编写软件,打造个性... 你有没有想过,手机里的那些神奇应用是怎么诞生的呢?没错,就是那些让你在闲暇时光刷刷视频、在通勤路上玩...
自动开机安卓系统,智能生活新篇... 你有没有想过,当你的安卓手机在清晨的第一缕阳光照耀下自动开机,那种轻松自在的感觉?想象不用再手动解锁...
真我平板x安卓系统,畅享智能生... 亲爱的读者们,你是否也在寻找一款既能满足你对平板电脑的期待,又能让你畅享安卓系统带来的无限乐趣的设备...
恒星安卓系统官网,引领未来智能... 亲爱的读者们,你是否曾好奇过那些闪耀在夜空中的星星,它们是如何在浩瀚的宇宙中熠熠生辉的呢?今天,我要...
u8安卓系统,功能与特色深度解... 你知道吗?在手机操作系统界,有一个小家伙可是相当受欢迎的,它就是U8安卓系统。今天,就让我带你来一探...
花椒安卓系统美颜功能,打造完美... 你有没有发现,现在拍照已经不仅仅是记录生活的工具了,它更是一种艺术创作呢!而在这其中,花椒安卓系统的...
戴尔平板升级安卓系统,畅享安卓... 你有没有发现,戴尔平板最近好像悄悄地来了一次大变身?没错,就是那个我们熟悉的戴尔平板,它现在竟然可以...
安卓助手怎么升级系统,畅享最新... 亲爱的安卓用户们,你是否也和我一样,对安卓系统的升级充满了期待和好奇呢?每次系统升级,都仿佛是给我们...
国产安卓系统的发展,国产安卓系... 你知道吗?在我国科技飞速发展的今天,国产安卓系统可是越来越受到大家的关注呢!它就像一颗冉冉升起的新星...