• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

奋斗的软件工程师

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

Java中使用通配符的限制与使用案例解析

在Java中,通配符(Wildcard)是一种在泛型(Generics)中使用的特殊类型参数,表示可以匹配任意类型。通配符主要有两种形式:? extends T和? super T。它们分别表示上界通配符和下界通配符。

上界通配符 ? extends T

使用? extends T声明的通配符表示该类型是T或T的子类型。这种通配符主要用于读取数据,不能向其中添加新的元素(除了null)。

例子:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<Integer>();
        
        // 不能添加元素
        // list.add(10); // 编译错误
        // list.add(10.5); // 编译错误

        // 可以读取元素
        Number num = list.get(0); // 编译通过
        
        // 可以添加 null
        list.add(null); // 编译通过
    }
}

在这个例子中,List<? extends Number>可以持有任何Number的子类类型的列表。你可以从这个列表中读取Number类型的元素,但不能向其中添加具体的元素,因为编译器无法确定列表实际持有的具体类型。

下界通配符 ? super T

使用? super T声明的通配符表示该类型是T或T的父类型。这种通配符主要用于写入数据。

例子:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? super Integer> list = new ArrayList<Number>();

        // 可以添加 Integer 及其子类的元素
        list.add(10); // 编译通过
        list.add(20); // 编译通过
        
        // 不能读取具体类型的元素(只能读取为Object)
        // Integer num = list.get(0); // 编译错误
        Object obj = list.get(0); // 编译通过
    }
}

在这个例子中,List<? super Integer>可以持有任何Integer的父类类型的列表。你可以向这个列表中添加Integer类型的元素,但从中读取时只能作为Object类型,因为编译器无法确定列表实际持有的具体类型。

结论

使用通配符声明的泛型类型在以下方面具有限制:

  • ? extends T:允许读取T或T的子类型的元素,不允许添加具体元素(除了null)。
  • ? super T:允许添加T或T的子类型的元素,但读取时只能作为Object类型。

这些限制是为了确保类型安全性,避免在运行时出现类型转换异常。

package com.itheima.demo;

import java.util.ArrayList;
import java.util.Collections;


public class Grade {
	private ArrayList<Teacher> teacherList = new ArrayList<>();
	private ArrayList<Student> studentList = new ArrayList<>();

	public Grade() {
		Teacher t1 = new Teacher("J1001", "张三", 36, 8800);
		Teacher t2 = new Teacher("J1002", "李四", 28, 6500);
		Teacher t3 = new Teacher("J1003", "王五", 38, 9600);
		Teacher t4 = new Teacher("J1004", "赵六", 48, 10800);

		Collections.addAll(teacherList,t1,t2,t3,t4);

		Student s1 = new Student("S1001", "王刚", 15, 130);
		Student s2 = new Student("S1002", "李婷", 16, 118);
		Student s3 = new Student("S1003", "张震", 14, 126);
		Student s4 = new Student("S1004", "赵宇", 15, 110);

		Collections.addAll(studentList,s1,s2,s3,s4);
	}

	public ArrayList<Teacher> getTeacherList() {
		return teacherList;
	}

	public void setTeacherList(ArrayList<Teacher> teacherList) {
		this.teacherList = teacherList;
	}

	public ArrayList<Student> getStudentList() {
		return studentList;
	}

	public void setStudentList(ArrayList<Student> studentList) {
		this.studentList = studentList;
	}

	public Grade(ArrayList<Teacher> teacherList, ArrayList<Student> studentList) {
		this.teacherList = teacherList;
		this.studentList = studentList;
	}

	public void showAllInfo(ArrayList<? extends Person>  list) {
		for (Person p : list) {
			if(p instanceof Student) {
				System.out.println(p);
			}

			if(p instanceof Teacher) {
				System.out.println(p);
			}
		}
	}

	public void sortPerson(ArrayList<? extends Person>  list) {
		if(!list.isEmpty()) {
			return;
		}

		 Person p = list.get(0);

		if(p instanceof Student) {
			for (int i = 0; i < list.size(); i++) {
				for (int j = 0; j < list.size() - i - 1; j++) {
					Student s1 = (Student) list.get(j);
					Student s2 = (Student) list.get(j + 1);
					if (s1.getScore() < s2.getScore()) {
						Student temp = s1;
						list.set(j, list.get(j + 1));
						list.set(j + 1, temp);
					}

				}
			}
		}

		if(p instanceof Teacher) {
			for (int i = 0; i < list.size(); i++) {
				for (int j = 0; j < list.size()-i-1; j++) {
					Teacher t1 = (Teacher) list.get(j);
					Teacher t2 = (Teacher) list.get(j+1);
					if(t1.getSalary()<t2.getSalary()) {
						Teacher temp = t1;
						list.set(j,list.get(j+1));
						list.set(j+1,temp);
					}

				}
			}

		}
	}
}

在你的代码中,通配符? extends Person确实会导致无法通过编译,因为通配符上界限制了列表的类型,这意味着编译器不能确定列表中元素的具体类型,只能保证是Person或其子类。因此,你不能对这个列表进行添加或修改操作。

详细解释

使用通配符? extends Person声明的列表只能保证其元素是Person或Person的子类,但不能确定具体的类型。这使得编译器无法保证向列表中添加元素时的类型安全性。

具体来说,以下代码会引起编译错误:

list.set(j, list.get(j + 1)); // 编译错误
list.set(j + 1, temp); // 编译错误

因为list的类型是ArrayList<? extends Person>,编译器无法确定list.get(j + 1)的具体类型,所以不允许调用set方法。

解决方案

为了能够对列表进行修改,可以使用下界通配符? super T。下界通配符允许向列表中添加或修改元素。

修改sortPerson方法

可以将sortPerson方法的参数类型修改为ArrayList<? super Student>或ArrayList<? super Teacher>,具体取决于你需要排序的列表类型。因为你的方法需要处理两种不同类型的列表,所以最好拆分成两个方法:

public void sortStudents(ArrayList<? super Student> list) {
    for (int i = 0; i < list.size(); i++) {
        for (int j = 0; j < list.size() - i - 1; j++) {
            Student s1 = (Student) list.get(j);
            Student s2 = (Student) list.get(j + 1);
            if (s1.getScore() < s2.getScore()) {
                list.set(j, s2);
                list.set(j + 1, s1);
            }
        }
    }
}

public void sortTeachers(ArrayList<? super Teacher> list) {
    for (int i = 0; i < list.size(); i++) {
        for (int j = 0; j < list.size() - i - 1; j++) {
            Teacher t1 = (Teacher) list.get(j);
            Teacher t2 = (Teacher) list.get(j + 1);
            if (t1.getSalary() < t2.getSalary()) {
                list.set(j, t2);
                list.set(j + 1, t1);
            }
        }
    }
}

调用方法

在调用时,你需要根据实际的列表类型来调用相应的方法:

Grade grade = new Grade();
ArrayList<Student> students = grade.getStudentList();
ArrayList<Teacher> teachers = grade.getTeacherList();

grade.sortStudents(students);
grade.sortTeachers(teachers);

总结

由于上界通配符? extends T的限制,无法对列表进行添加或修改操作。通过使用下界通配符? super T,可以解决这个问题,从而允许对列表进行添加或修改操作。

这种情况可以归结为泛型的约束和限制。Java中的泛型通过类型参数的使用来实现类型安全和代码重用,但也会引入一些操作上的限制。具体来说,上界通配符? extends T和下界通配符? super T都有其各自的约束和使用场景。

泛型的约束和限制

上界通配符 ? extends T

  • 约束:指定类型必须是T或T的子类型。
  • 限制:不能向集合中添加元素(除了null),只能读取元素。
  • 适用场景:当你需要从集合中读取数据且不修改集合时使用。
List<? extends Number> list = new ArrayList<Integer>();

// 不能添加元素
// list.add(10); // 编译错误
// list.add(10.5); // 编译错误

// 可以读取元素
Number num = list.get(0); // 编译通过

// 可以添加 null
list.add(null); // 编译通过

下界通配符 ? super T

  • 约束:指定类型必须是T或T的父类型。
  • 限制:可以向集合中添加T或T的子类型的元素,但读取时只能读取为Object类型。
  • 适用场景:当你需要向集合中写入数据且不需要读取具体类型时使用。
List<? super Integer> list = new ArrayList<Number>();

// 可以添加 Integer 及其子类的元素
list.add(10); // 编译通过
list.add(20); // 编译通过

// 不能读取具体类型的元素(只能读取为Object)
Object obj = list.get(0); // 编译通过

posted on 2024-08-03 16:36  周政然  阅读(238)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3