目录
1. 泛型概述
1.1 生活中的例子
-
举例1:中药店,每个抽屉外面贴着标签
-
举例2:超市购物架上很多瓶子,每个瓶子装的是什么,有标签
-
举例3:家庭厨房中:
Java中的泛型,就类似于上述场景中的
标签
。
1.2 泛型的引入
在Java中,我们在声明方法时,当在完成方法功能时如果有未知的数据
需要参与,这些未知的数据需要在调用方法时才能确定,那么我们把这样的数据通过形参
表示。在方法体中,用这个形参名来代表那个未知的数据,而调用者在调用时,对应的传入实参
就可以了。
受以上启发,JDK1.5设计了泛型的概念。泛型即为“类型参数
”,这个类型参数在声明它的类、接口或方法中,代表未知的某种通用类型。
举例1:
集合类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK5.0之前只能把元素类型设计为Object,JDK5.0时Java引入了“参数化类型(Parameterized type)”的概念,允许我们在创建集合时指定集合元素的类型。比如:List<String>
,这表明该List只能保存字符串类型的对象。
使用集合存储数据时,除了元素的类型不确定,其他部分是确定的(例如关于这个元素如何保存,如何管理等)。
举例2:
java.lang.Comparable
接口和java.util.Comparator
接口,是用于比较对象大小的接口。这两个接口只是限定了当一个对象大于另一个对象时返回正整数,小于返回负整数,等于返回0,但是并不确定是什么类型的对象比较大小。JDK5.0之前只能用Object类型表示,使用时既麻烦又不安全,因此 JDK5.0 给它们增加了泛型。
其中<T>
就是类型参数,即泛型。
所谓泛型,就是允许在定义类、接口时通过一个
标识
表示类中某个属性的类型
或者是某个方法的返回值或参数的类型
。这个类型参数将在使用时(例如,继承或实现这个接口、创建对象或调用方法时)确定(即传入实际的类型参数,也称为类型实参)。
2. 使用泛型举例
自从JDK5.0引入泛型的概念之后,对之前核心类库中的API做了很大的修改,例如:JDK5.0改写了集合框架中的全部接口和类、java.lang.Comparable接口、java.util.Comparator接口、Class类等。为这些接口、类增加了泛型支持,从而可以在声明变量、创建对象时传入类型实参。
2.1 集合中使用泛型
2.1.1 举例
集合中没有使用泛型时:
集合中使用泛型时:
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。即,把不安全的因素在编译期间就排除了,而不是运行期;既然通过了编译,那么类型一定是符合要求的,就避免了类型转换。
同时,代码更加简洁、健壮。
把一个集合中的内容限制为一个特定的数据类型,这就是generic背后的核心思想。
举例:
//泛型在List中的使用
@Test
public void test1(){
//举例:将学生成绩保存在ArrayList中
//标准写法:
//ArrayList<Integer> list = new ArrayList<Integer>();
//jdk7的新特性:类型推断
ArrayList<Integer> list = new ArrayList<>();
list.add(56); //自动装箱
list.add(76);
list.add(88);
list.add(89);
//当添加非Integer类型数据时,编译不通过
//list.add("Tom");//编译报错
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
//不需要强转,直接可以获取添加时的元素的数据类型
Integer score = iterator.next();
System.out.println(score);
}
}
举例:
//泛型在Map中的使用
@Test
public void test2(){
HashMap<String,Integer> map = new HashMap<>();
map.put("Tom",67);
map.put("Jim",56);
map.put("Rose",88);
//编译不通过
// map.put(67,"Jack");
//遍历key集
Set<String> keySet = map.keySet();
for(String str:keySet){
System.out.println(str);
}
//遍历value集
Collection<Integer> values = map.values();
Iterator<Integer> iterator = values.iterator();
while(iterator.hasNext()){
Integer value = iterator.next();
System.out.println(value);
}
//遍历entry集
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator1 = entrySet.iterator();
while(iterator1.hasNext()){
Map.Entry<String, Integer> entry = iterator1.next();
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + ":" + value);
}
}
2.1.2 练习
练习1:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import java.util.function.Predicate;
public class TestNumber {
public static void main(String[] args) {
ArrayList<Integer> coll = new ArrayList<Integer>();
Random random = new Random();
for (int i = 1; i <= 5 ; i++) {
coll.add(random.nextInt(100));
}
System.out.println("coll中5个随机数是:");
for (Integer integer : coll) {
System.out.println(integer);
}
//方式1:使用集合的removeIf方法删除偶数
coll.removeIf(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer % 2 == 0;
}
});
//方式2:调用Iterator接口的remove()方法
//Iterator<Integer> iterator1 = coll.iterator();
//while(coll.hasNext()){
// Integer i = coll.next();
// if(i % 2 == 0){
// coll.remove();
// }
//}
System.out.println("coll中删除偶数后:");
Iterator<Integer> iterator = coll.iterator();
while(iterator.hasNext()){
Integer number = iterator.next();
System.out.println(number);
}
}
}
2.2 比较器中使用泛型
2.2.1 举例
public 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;
}
@Override
public String toString() {
return "Circle [radius=" + radius + "]";
}
}
使用泛型之前:
import java.util.Comparator;
class CircleComparator implements Comparator{
@Override
public int compare(Object o1, Object o2) {
//强制类型转换
Circle c1 = (Circle) o1;
Circle c2 = (Circle) o2;
return Double.compare(c1.getRadius(), c2.getRadius());
}
}
//测试:
public class TestNoGeneric {
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
}
}
使用泛型之后:
import java.util.Comparator;
class CircleComparator1 implements Comparator<Circle> {
@Override
public int compare(Circle o1, Circle o2) {
//不再需要强制类型转换,代码更简洁
return Double.compare(o1.getRadius(), o2.getRadius());
}
}
//测试类
public class TestHasGeneric {
public static void main(String[] args) {
CircleComparator1 com = new CircleComparator1();
System.out.println(com.compare(new Circle(1), new Circle(2)));
//System.out.println(com.compare("圆1", "圆2"));
//编译错误,因为"圆1", "圆2"不是Circle类型,是String类型,编译器提前报错,
//而不是冒着风险在运行时再报错。
}
}
2.2.2 练习
MyDate.jave
package exer1;
/**
* ClassName:IntelliJ IDEA
* Description:
* 2、MyDate类包含:
* private成员变量year,month,day,并为每一个属性定义getter、setter方法
* @Author zyjstart
* @Create:2024/10/5 16:02
*/
public class MyDate {
private int year;
private int month;
private int day;
public MyDate() {
}
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
@Override
public String toString() {
return year + "年" + month + "月" + day + "日";
}
}
Employee.java
package exer1;
/**
* ClassName:IntelliJ IDEA
* Description:
* 1、定义一个Employee类:
* 该类包含:private成员变量name,age,birthday,其中birthday为MyDate类的对象:
* 并为每一个属性定义getter,setter方法
* 并重写toString方法输出name,age,birthday
* @Author zyjstart
* @Create:2024/10/5 16:00
*/
public class Employee implements Comparable<Employee>{
private String name;
private int age;
private MyDate birthday;
public Employee() {
}
public Employee(String name, int age, MyDate birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
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;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
//按照name从高到低排序
@Override
public int compareTo(Employee o) {
return this.name.compareTo(o.name);
}
}
EmployeeTest.java
package exer1;
import org.junit.Test;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/**
* ClassName:IntelliJ IDEA
* Description:
* 3、创建该类的5个对象,并把这些对象放入TreeSet集合中(TreeSet 需使用泛型来定义)
*
* 4、分别按以下两种方式对集合中的元素进行排序,并遍历输出:
* 1)、使Employee实现Comparable接口,并按name排序
* 2)、创建TreeSet时传入Comparator对象,按生日日期的先后排序
* @Author zyjstart
* @Create:2024/10/5 16:05
*/
public class EmployeeTest {
//1)、使Employee实现Comparable接口,并按name排序
@Test
public void test1(){
TreeSet<Employee> set = new TreeSet<>();
Employee e1 = new Employee("Hanmeimei",18,new MyDate(1998,12,16));
Employee e2 = new Employee("Gaoqiqi",25,new MyDate(2002,11,19));
Employee e3 = new Employee("Daleiju",34,new MyDate(1944,6,24));
Employee e4 = new Employee("Zixianxina",17,new MyDate(1999,8,27));
Employee e5 = new Employee("Yinana",37,new MyDate(2011,1,31));
set.add(e1);
set.add(e2);
set.add(e3);
set.add(e4);
set.add(e5);
//遍历
Iterator<Employee> iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
//2)、创建TreeSet时传入Comparator对象,按生日日期的先后排序
@Test
public void test2() {
Comparator<Employee> comparator = new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
// 先比较年
int yearDistince = o1.getBirthday().getYear() - o2.getBirthday().getYear();
if (yearDistince != 0){ // 如果年份不相等,返回结果,相等,则判断月份
return yearDistince;
}
// 比较月
int monthDistince = o1.getBirthday().getMonth() - o2.getBirthday().getMonth();
if (monthDistince != 0){
return monthDistince;
}
return o1.getBirthday().getDay() - o2.getBirthday().getDay();
}
};
TreeSet<Employee> set = new TreeSet<>(comparator);
Employee e1 = new Employee("Hanmeimei",18,new MyDate(1999,12,16));
Employee e2 = new Employee("Gaoqiqi",25,new MyDate(2002,11,19));
Employee e3 = new Employee("Daleiju",34,new MyDate(1944,6,24));
Employee e4 = new Employee("Zixianxina",17,new MyDate(1999,8,27));
Employee e5 = new Employee("Yinana",37,new MyDate(2011,1,31));
set.add(e1);
set.add(e2);
set.add(e3);
set.add(e4);
set.add(e5);
//遍历
Iterator<Employee> iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
在EmployeeTest.java中有两个测试方法,分别对应两个目标要求
test1按姓名排序,输出
test2按出生年月排序,输出