jdk17至21——switch表达式的模式匹配

switch表达式的模式匹配从jdk17开始提出,到jdk20经过4次预览,最终在jdk21成为正式api,增加了null匹配和类型匹配,以及使用when条件的守卫模式,对枚举类型也进行了优化,让switch的功能更强大;

官方说明:JEP 441: Pattern Matching for switch

null加入case判断,不再需要提前判断null了,必要时也可将null跟default放一个case:

	@Test
	void test() {
		String s = null;
		switch (s) {
		case null -> System.out.println("s=null");
		default -> System.out.println("default");
		}
		Object o = null;
		String type = switch(o) {
		case String str -> "str";
		case null, default -> "null or default";
		};
		System.out.println(type);
	}

case在模式匹配的时候,还可以使用守卫模式when进一步限制条件(只有模式标签才可以使用when):

	@Test
	void test2() {
		Set<Object> objSet = HashSet.newHashSet(5);
		objSet.add(null);
		objSet.add(123);
		objSet.add(5);
		objSet.add("abc");
		objSet.add("hello world");
		objSet.forEach(obj -> {
			switch(obj) {
			case null -> System.out.println("obj=null");
			case Integer i when i > 10 -> System.out.println("int obj > 10 obj=%d".formatted(i));
			case Integer i -> System.out.println("int obj=%d".formatted(i));
			case String s when s.startsWith("hello") -> System.out.println("string hello* obj=%s".formatted(s));
			case String s -> System.out.println("string obj=%s".formatted(s));
			default -> System.out.println("default obj=%s class:%s".formatted(obj, obj.getClass()));
			}
		});
	}

swtich表达式对枚举类型也进行了优化,表达式参数是枚举类型的时候,case可以直接使用枚举值也可以带上枚举类名,表达式参数不是枚举类型的时候,比如是枚举继承的父类或者实现的接口时,case里的枚举值必须带枚举类名:(枚举的时候尽量不用default,这样能很快发现swtich缺失的新加的枚举值)

interface I{}
enum E implements I {A,B}
	@Test
	void test3() {
		E e = E.A;
		switch(e) { // 参数直接是枚举类型
		case A -> System.out.println("E.A"); // 可以直接使用枚举值
		case E.B -> System.out.println("E.B"); // 也可以带上枚举类
		}
		I i = E.B;
		switch(i) { // 参数不是枚举类型
		case E.A -> System.out.println("E.A"); // 只能使用显式枚举类名
		case E.B -> System.out.println("E.B");
//		case B -> System.out.println("E.B"); // 直接使用枚举值报错
		case I x -> System.out.println("I");
		}
	}

一些注意事项:

使用模式匹配或null的case必须要有穷尽性(表达式类型是char, byte, short, int, Character, Byte, Short, Integer, String,enum以外的),case要覆盖所有可能的匹配类型,否则需要用default补充剩余可能的case;

class P{}
class C1 extends P{}
class C2 extends P{}
	@Test
	void test4() {
		P obj = new P();
		switch(obj){ // obj有3种可能性:C1、C2、P(不排除还有其他可能)
		case C1 c1 -> System.out.println("C1");
		case C2 c2 -> System.out.println("C2");
		case P p -> System.out.println("P"); // 父类可覆盖所有子类可能性
		}
		switch(obj) {
		case C2 c2 -> System.out.println("_C2");
		default -> System.out.println("default"); // default补充了可能是C1或者P
		}
	}

由于父类可以覆盖子类,所以上面的C1和C2可以注释掉也不会报错:

如果switch表达式里的参数是sealed类型,case可以根据允许的子类穷尽,可以避免default或者父类的不确定性:

sealed interface MySI permits A, B {}
record A(int i) implements MySI {}
record B(String s) implements MySI {}
	@Test
	void test5() {
		MySI mySI = new A(1234);
		switch(mySI) { // MySi的permits只有A和B,没有其他可能,caseA和B就穷尽
		case A(var i) -> System.out.println("A.i:" + i);
		case B b -> System.out.println("B:" + b);
		}
	}

另一个需要注意的点是case的条件顺序,前面的case条件不能完全覆盖后面的case条件,否则的后面的条件就永远不会执行(类似try的catch参数);(现代开发工具都会表明这种错误)

相同类型的case通常是先用常量,再用带when的守卫模式,最后用类型本身;

相同范围的case也不能用两次,例如case Object 跟最后的default就可能冲突;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值