搞Java开发这么多年,鹏磊我最烦的就是处理原始类型(primitive types)的时候,特别是用instanceof判断类型,那叫一个麻烦。以前想判断一个Object是不是int类型,得先装箱成Integer,然后再判断,写出来的代码又臭又长,看着就难受。
现在好了,JDK 24 的 JEP 488 终于把这个痛点给解决了。这个特性是第2次预览,让instanceof和switch语句全面支持原始类型,代码能简洁不少,写起来也顺手多了。兄弟们别磨叽,咱这就开始整活,把这个特性给整明白。
什么是原始类型模式匹配
先说说啥是原始类型模式匹配。简单来说,就是可以在instanceof和switch语句里直接匹配原始类型(int、long、double、float、boolean、byte、short、char这些),不用再装箱成包装类型了。
以前你想判断一个Object是不是int,得这么写:
// 老写法,得先装箱
Object value = 42;
if (value instanceof Integer) {
Integer i = (Integer) value; // 还得强制转换
int num = i.intValue(); // 再拆箱
System.out.println("这是整数: " + num);
}
现在JDK 24里,直接就能这么写:
// 新写法,直接匹配原始类型
Object value = 42;
if (value instanceof int i) { // 直接匹配int,变量i自动绑定
System.out.println("这是整数: " + i); // 直接用,不用拆箱
}
是不是清爽多了?代码量少了,逻辑也更清晰,不用再搞那些装箱拆箱的破事了。
JEP 488 的核心改进
JEP 488 主要做了两件事:一个是让instanceof支持原始类型模式,另一个是让switch表达式和语句也支持原始类型。咱一个个来看。
instanceof 支持原始类型
instanceof现在可以直接匹配原始类型了,语法是 instanceof 原始类型 变量名。匹配成功后,变量会自动绑定到对应的原始类型值,不用再手动转换。
public class InstanceofPrimitiveDemo {
public static void processValue(Object value) {
// 匹配int类型
if (value instanceof int i) {
System.out.println("整数: " + i);
// i 已经是int类型,可以直接用
int doubled = i * 2;
System.out.println("翻倍: " + doubled);
}
// 匹配long类型
else if (value instanceof long l) {
System.out.println("长整数: " + l);
// l 已经是long类型
long squared = l * l;
System.out.println("平方: " + squared);
}
// 匹配double类型
else if (value instanceof double d) {
System.out.println("双精度浮点数: " + d);
// d 已经是double类型
double rounded = Math.round(d);
System.out.println("四舍五入: " + rounded);
}
// 匹配float类型
else if (value instanceof float f) {
System.out.println("单精度浮点数: " + f);
}
// 匹配boolean类型
else if (value instanceof boolean b) {
System.out.println("布尔值: " + b);
// b 已经是boolean类型
if (b) {
System.out.println("这是true");
}
}
// 匹配byte类型
else if (value instanceof byte by) {
System.out.println("字节: " + by);
}
// 匹配short类型
else if (value instanceof short s) {
System.out.println("短整数: " + s);
}
// 匹配char类型
else if (value instanceof char c) {
System.out.println("字符: " + c);
}
else {
System.out.println("未知类型");
}
}
public static void main(String[] args) {
// 测试各种原始类型
processValue(42); // int
processValue(100L); // long
processValue(3.14); // double
processValue(2.5f); // float
processValue(true); // boolean
processValue((byte) 127); // byte
processValue((short) 1000); // short
processValue('A'); // char
processValue("字符串"); // String,不匹配
}
}
这个特性用起来确实爽,代码简洁多了,逻辑也更清晰。特别是处理那种从外部系统传过来的数据,类型不确定的时候,用这个特性能省不少事。
switch 支持原始类型
switch表达式和语句现在也支持原始类型模式了,可以在case标签里直接匹配原始类型。
public class SwitchPrimitiveDemo {
// switch表达式支持原始类型
public static int processWithSwitchExpression(Object value) {
return switch (value) {
case int i -> {
// i 是int类型,可以直接用
System.out.println("处理整数: " + i);
yield i * 2; // 返回翻倍后的值
}
case long l -> {
System.out.println("处理长整数: " + l);
yield (int)(l / 2); // 返回除以2后的值,转成int
}
case double d -> {
System.out.println("处理双精度浮点数: " + d);
yield (int) Math.round(d); // 四舍五入后转int
}
case float f -> {
System.out.println("处理单精度浮点数: " + f);
yield (int) f; // 直接转int
}
case boolean b -> {
System.out.println("处理布尔值: " + b);
yield b ? 1 : 0; // true返回1,false返回0
}
case byte by -> {
System.out.println("处理字节: " + by);
yield (int) by; // 转成int返回
}
case short s -> {
System.out.println("处理短整数: " + s);
yield (int) s; // 转成int返回
}
case char c -> {
System.out.println("处理字符: " + c);
yield (int) c; // 转成ASCII码返回
}
default -> {
System.out.println("未知类型,返回0");
yield 0;
}
};
}
// switch语句也支持原始类型
public static void processWithSwitchStatement(Object value) {
switch (value) {
case int i -> {
// 处理int类型
if (i > 0) {
System.out.println("正整数: " + i);
} else if (i < 0) {
System.out.println("负整数: " + i);
} else {
System.out.println("零");
}
}
case long l -> {
// 处理long类型
System.out.println("长整数范围: " +
(l > Integer.MAX_VALUE ? "超出int范围" : "在int范围内"));
}
case double d -> {
// 处理double类型
if (Double.isNaN(d)) {
System.out.println("不是数字");
} else if (Double.isInfinite(d)) {
System.out.println("无穷大");
} else {
System.out.println("正常浮点数: " + d);
}
}
case boolean b -> {
// 处理boolean类型
String result = b ? "真" : "假";
System.out.println("布尔值: " + result);
}
default -> {
System.out.println("其他类型或null");
}
}
}
public static void main(String[] args) {
// 测试switch表达式
System.out.println("=== switch表达式测试 ===");
System.out.println("结果: " + processWithSwitchExpression(42));
System.out.println("结果: " + processWithSwitchExpression(100L));
System.out.println("结果: " + processWithSwitchExpression(3.14));
System.out.println("结果: " + processWithSwitchExpression(true));
System.out.println("\n=== switch语句测试 ===");
processWithSwitchStatement(42);
processWithSwitchStatement(-10);
processWithSwitchStatement(0);
processWithSwitchStatement(100L);
processWithSwitchStatement(3.14);
processWithSwitchStatement(Double.NaN);
processWithSwitchStatement(true);
}
}
switch支持原始类型后,代码能更简洁,特别是那种需要根据不同类型做不同处理的场景,用switch比一堆if-else清爽多了。
类型转换和自动装箱拆箱
原始类型模式匹配在处理类型转换的时候,会自动处理装箱拆箱,不用咱手动搞了。但有些细节得注意,咱看看。
自动类型转换
当匹配到原始类型后,变量会自动绑定到对应的原始类型值。如果原始值是包装类型(比如Integer),会自动拆箱;如果是原始类型,就直接用。
public class TypeConversionDemo {
public static void demonstrateAutoConversion(Object value) {
// 如果value是Integer对象,会自动拆箱成int
if (value instanceof int i) {
System.out.println("匹配到int: " + i);
// i 是原始int类型,不是Integer对象
System.out.println("类型: " + i.getClass()); // 这行会编译错误,因为int是原始类型
}
// 如果value是Long对象,会自动拆箱成long
if (value instanceof long l) {
System.out.println("匹配到long: " + l);
// l 是原始long类型
}
// 如果value是Double对象,会自动拆箱成double
if (value instanceof double d) {
System.out.println("匹配到double: " + d);
// d 是原始double类型
}
}
public static void main(String[] args) {
// 测试包装类型,会自动拆箱
demonstrateAutoConversion(Integer.valueOf(42));
demonstrateAutoConversion(Long.valueOf(100L));
demonstrateAutoConversion(Double.valueOf(3.14));
// 测试原始类型(虽然Object不能直接存原始类型,但这里演示概念)
// 实际使用中,原始类型会被自动装箱
Integer intObj = 42; // 自动装箱
demonstrateAutoConversion(intObj);
}
}
类型兼容性
原始类型模式匹配的时候,得注意类型兼容性。比如int和Integer都能匹配instanceof int,但String就不能匹配。
public class TypeCompatibilityDemo {
public static void checkCompatibility(Object value) {
// int和Integer都能匹配
if (value instanceof int i) {
System.out.println("匹配int: " + i);
}
// long和Long都能匹配
if (value instanceof long l) {
System.out.println("匹配long: " + l);
}
// double和Double都能匹配
if (value instanceof double d) {
System.out.println("匹配double: " + d);
}
// 但String不能匹配原始类型
if (value instanceof String s) {
System.out.println("这是字符串: " + s);
// 下面这行会编译错误,因为String不能匹配int
// if (s instanceof int i) { ... }
}
}
public static void main(String[] args) {
checkCompatibility(42); // int,能匹配
checkCompatibility(Integer.valueOf(42)); // Integer,也能匹配
checkCompatibility("hello"); // String,不能匹配原始类型
}
}
实际应用场景
原始类型模式匹配在实际开发中能解决不少问题,特别是处理那种类型不确定的数据。咱看看几个常见的应用场景。
场景1:处理JSON数据
从JSON解析出来的数据,类型经常不确定,用原始类型模式匹配能简化代码。
import java.util.Map;
public class JsonProcessingDemo {
// 模拟从JSON解析出来的数据
public static void processJsonValue(Object jsonValue) {
// 根据不同类型做不同处理
String result = switch (jsonValue) {
case int i -> {
// 整数类型,可能是年龄、数量等
System.out.println("解析到整数: " + i);
yield "整数: " + i;
}
case long l -> {
// 长整数类型,可能是时间戳、大数值等
System.out.println("解析到长整数: " + l);
yield "长整数: " + l;
}
case double d -> {
// 浮点数类型,可能是价格、坐标等
System.out.println("解析到浮点数: " + d);
yield "浮点数: " + d;
}
case boolean b -> {
// 布尔类型,可能是开关、状态等
System.out.println("解析到布尔值: " + b);
yield "布尔值: " + b;
}
case String s -> {
// 字符串类型
System.out.println("解析到字符串: " + s);
yield "字符串: " + s;
}
case null -> {
System.out.println("值为null");
yield "null";
}
default -> {
System.out.println("未知类型: " + jsonValue.getClass());
yield "未知类型";
}
};
System.out.println("处理结果: " + result);
}
// 处理JSON对象中的字段
public static void processJsonField(String key, Object value) {
System.out.println("处理字段: " + key);
// 根据字段名和类型做不同处理
if ("age".equals(key) && value instanceof int age) {
// age字段,必须是int类型
if (age < 0 || age > 150) {
System.out.println("警告: 年龄值异常: " + age);
} else {
System.out.println("年龄: " + age);
}
} else if ("price".equals(key) && value instanceof double price) {
// price字段,必须是double类型
if (price < 0) {
System.out.println("警告: 价格不能为负: " + price);
} else {
System.out.println("价格: " + price);
}
} else if ("isActive".equals(key) && value instanceof boolean isActive) {
// isActive字段,必须是boolean类型
System.out.println("激活状态: " + (isActive ? "激活" : "未激活"));
} else {
System.out.println("其他字段: " + key + " = " + value);
}
}
public static void main(String[] args) {
// 模拟处理JSON数据
processJsonValue(25); // 年龄
processJsonValue(1699123456789L); // 时间戳
processJsonValue(99.99); // 价格
processJsonValue(true); // 状态
processJsonValue("用户名"); // 字符串
processJsonValue(null); // null值
System.out.println("\n=== 处理JSON字段 ===");
processJsonField("age", 25);
processJsonField("price", 99.99);
processJsonField("isActive", true);
processJsonField("name", "张三");
}
}
场景2:数据验证和转换
在处理用户输入或者外部数据的时候,经常需要验证类型和做转换,原始类型模式匹配能让代码更简洁。
public class DataValidationDemo {
// 验证并转换数值
public static Integer validateAndConvert(Object input) {
return switch (input) {
case int i -> {
// 已经是int,直接返回
if (i < 0) {
System.out.println("警告: 负数: " + i);
}
yield i;
}
case Integer integer -> {
// Integer对象,拆箱后返回
int value = integer;
if (value < 0) {
System.out.println("警告: 负数: " + value);
}
yield value;
}
case long l -> {
// long类型,检查范围后转int
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
System.out.println("错误: 超出int范围: " + l);
yield null;
}
yield (int) l;
}
case double d -> {
// double类型,四舍五入后转int
if (Double.isNaN(d) || Double.isInfinite(d)) {
System.out.println("错误: 无效的浮点数: " + d);
yield null;
}
yield (int) Math.round(d);
}
case String s -> {
// 字符串,尝试解析
try {
int value = Integer.parseInt(s);
yield value;
} catch (NumberFormatException e) {
System.out.println("错误: 无法解析为整数: " + s);
yield null;
}
}
default -> {
System.out.println("错误: 不支持的类型: " +
(input != null ? input.getClass() : "null"));
yield null;
}
};
}
// 验证布尔值
public static Boolean validateBoolean(Object input) {
return switch (input) {
case boolean b -> b; // 直接返回
case Boolean bool -> bool; // Boolean对象,直接返回
case String s -> {
// 字符串,尝试解析
if ("true".equalsIgnoreCase(s) || "1".equals(s)) {
yield true;
} else if ("false".equalsIgnoreCase(s) || "0".equals(s)) {
yield false;
} else {
System.out.println("错误: 无法解析为布尔值: " + s);
yield null;
}
}
case int i -> i != 0; // 整数,非0为true
default -> {
System.out.println("错误: 不支持的类型: " +
(input != null ? input.getClass() : "null"));
yield null;
}
};
}
public static void main(String[] args) {
// 测试数据验证和转换
System.out.println("=== 数值验证和转换 ===");
System.out.println("结果: " + validateAndConvert(42));
System.out.println("结果: " + validateAndConvert(Integer.valueOf(100)));
System.out.println("结果: " + validateAndConvert(1000L));
System.out.println("结果: " + validateAndConvert(3.7));
System.out.println("结果: " + validateAndConvert("123"));
System.out.println("结果: " + validateAndConvert("abc"));
System.out.println("\n=== 布尔值验证 ===");
System.out.println("结果: " + validateBoolean(true));
System.out.println("结果: " + validateBoolean(Boolean.FALSE));
System.out.println("结果: " + validateBoolean("true"));
System.out.println("结果: " + validateBoolean("1"));
System.out.println("结果: " + validateBoolean(42));
System.out.println("结果: " + validateBoolean(0));
}
}
场景3:类型分发和处理
在处理不同类型数据的时候,用原始类型模式匹配能实现更清晰的类型分发。
public class TypeDispatchDemo {
// 统一的处理接口
public static void processData(Object data) {
// 根据类型分发到不同的处理逻辑
switch (data) {
case int i -> processInt(i);
case long l -> processLong(l);
case double d -> processDouble(d);
case boolean b -> processBoolean(b);
case String s -> processString(s);
case null -> processNull();
default -> processUnknown(data);
}
}
// 处理int类型
private static void processInt(int value) {
System.out.println("处理整数: " + value);
// 可以做一些int特有的处理
if (value % 2 == 0) {
System.out.println(" 是偶数");
} else {
System.out.println(" 是奇数");
}
}
// 处理long类型
private static void processLong(long value) {
System.out.println("处理长整数: " + value);
// 可以做一些long特有的处理
if (value > Integer.MAX_VALUE) {
System.out.println(" 超出int范围");
}
}
// 处理double类型
private static void processDouble(double value) {
System.out.println("处理浮点数: " + value);
// 可以做一些double特有的处理
if (value == (int) value) {
System.out.println(" 是整数");
} else {
System.out.println(" 是小数");
}
}
// 处理boolean类型
private static void processBoolean(boolean value) {
System.out.println("处理布尔值: " + value);
// 可以做一些boolean特有的处理
System.out.println(" 逻辑值: " + (value ? "真" : "假"));
}
// 处理String类型
private static void processString(String value) {
System.out.println("处理字符串: " + value);
// 可以做一些String特有的处理
System.out.println(" 长度: " + value.length());
}
// 处理null
private static void processNull() {
System.out.println("处理null值");
}
// 处理未知类型
private static void processUnknown(Object value) {
System.out.println("处理未知类型: " + value.getClass());
}
public static void main(String[] args) {
// 测试类型分发
processData(42);
processData(100L);
processData(3.14);
processData(true);
processData("hello");
processData(null);
processData(new Object());
}
}
与现有特性的结合
原始类型模式匹配可以和其他Java特性结合使用,比如记录类(Record)、密封类(Sealed Class)等,能写出更强大的代码。
与记录类结合
记录类和原始类型模式匹配结合,能更优雅地处理数据。
// 定义一个记录类
public record Point(int x, int y) {
// 记录类可以包含方法
public double distance() {
return Math.sqrt(x * x + y * y);
}
}
public class RecordWithPrimitiveDemo {
// 处理不同类型的坐标数据
public static void processCoordinate(Object coord) {
switch (coord) {
case Point p -> {
// 匹配记录类
System.out.println("记录类坐标: (" + p.x() + ", " + p.y() + ")");
System.out.println("距离: " + p.distance());
}
case int[] arr when arr.length == 2 -> {
// 匹配int数组,且长度为2
System.out.println("数组坐标: (" + arr[0] + ", " + arr[1] + ")");
}
case double[] arr when arr.length == 2 -> {
// 匹配double数组,且长度为2
System.out.println("浮点数组坐标: (" + arr[0] + ", " + arr[1] + ")");
}
default -> {
System.out.println("不支持的坐标格式");
}
}
}
// 处理混合类型的数据
public static void processMixedData(Object data) {
switch (data) {
case int i -> {
System.out.println("整数: " + i);
}
case double d -> {
System.out.println("浮点数: " + d);
}
case Point p -> {
System.out.println("点: " + p);
}
case String s -> {
System.out.println("字符串: " + s);
}
default -> {
System.out.println("其他类型");
}
}
}
public static void main(String[] args) {
// 测试记录类
Point point = new Point(3, 4);
processCoordinate(point);
// 测试数组
int[] intCoords = {5, 6};
processCoordinate(intCoords);
double[] doubleCoords = {7.5, 8.5};
processCoordinate(doubleCoords);
// 测试混合数据
System.out.println("\n=== 混合数据处理 ===");
processMixedData(42);
processMixedData(3.14);
processMixedData(new Point(1, 2));
processMixedData("hello");
}
}
与守卫模式结合
原始类型模式匹配可以和守卫模式(guarded pattern)结合,实现更复杂的条件判断。
public class GuardedPatternDemo {
// 使用守卫模式进行条件判断
public static void processWithGuard(Object value) {
switch (value) {
// 匹配int,且值大于0
case int i when i > 0 -> {
System.out.println("正整数: " + i);
}
// 匹配int,且值小于0
case int i when i < 0 -> {
System.out.println("负整数: " + i);
}
// 匹配int,且值为0
case int i when i == 0 -> {
System.out.println("零");
}
// 匹配long,且值在int范围内
case long l when l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE -> {
System.out.println("长整数(在int范围内): " + l);
}
// 匹配long,且值超出int范围
case long l -> {
System.out.println("长整数(超出int范围): " + l);
}
// 匹配double,且是有效数字
case double d when !Double.isNaN(d) && !Double.isInfinite(d) -> {
System.out.println("有效浮点数: " + d);
}
// 匹配double,且是NaN
case double d when Double.isNaN(d) -> {
System.out.println("NaN(不是数字)");
}
// 匹配double,且是无穷大
case double d when Double.isInfinite(d) -> {
System.out.println("无穷大: " + d);
}
// 匹配boolean
case boolean b -> {
System.out.println("布尔值: " + b);
}
default -> {
System.out.println("其他类型");
}
}
}
// 处理数值范围
public static String categorizeNumber(Object value) {
return switch (value) {
case int i when i >= 0 && i <= 100 -> "小整数: " + i;
case int i when i > 100 && i <= 1000 -> "中等整数: " + i;
case int i when i > 1000 -> "大整数: " + i;
case int i -> "负整数: " + i;
case long l when l > Integer.MAX_VALUE -> "超大整数: " + l;
case long l -> "长整数: " + l;
case double d when d >= 0 && d < 1 -> "小数: " + d;
case double d when d >= 1 && d < 100 -> "普通浮点数: " + d;
case double d -> "大浮点数: " + d;
default -> "非数值类型";
};
}
public static void main(String[] args) {
// 测试守卫模式
System.out.println("=== 守卫模式测试 ===");
processWithGuard(42);
processWithGuard(-10);
processWithGuard(0);
processWithGuard(100L);
processWithGuard(3000000000L);
processWithGuard(3.14);
processWithGuard(Double.NaN);
processWithGuard(Double.POSITIVE_INFINITY);
processWithGuard(true);
System.out.println("\n=== 数值分类 ===");
System.out.println(categorizeNumber(50));
System.out.println(categorizeNumber(500));
System.out.println(categorizeNumber(5000));
System.out.println(categorizeNumber(-10));
System.out.println(categorizeNumber(3000000000L));
System.out.println(categorizeNumber(0.5));
System.out.println(categorizeNumber(50.5));
System.out.println(categorizeNumber(500.5));
}
}
性能考虑
原始类型模式匹配在性能上也有优势,因为避免了不必要的装箱拆箱操作,减少了对象创建和内存分配。
性能对比
咱写个简单的性能测试,看看新旧写法的性能差异。
public class PerformanceDemo {
// 旧写法:使用包装类型
public static int processOldWay(Object value) {
if (value instanceof Integer) {
Integer i = (Integer) value;
return i.intValue() * 2;
} else if (value instanceof Long) {
Long l = (Long) value;
return (int)(l.longValue() / 2);
} else if (value instanceof Double) {
Double d = (Double) value;
return (int) Math.round(d.doubleValue());
}
return 0;
}
// 新写法:使用原始类型模式匹配
public static int processNewWay(Object value) {
return switch (value) {
case int i -> i * 2;
case long l -> (int)(l / 2);
case double d -> (int) Math.round(d);
default -> 0;
};
}
public static void main(String[] args) {
// 准备测试数据
Object[] testData = new Object[1000000];
for (int i = 0; i < testData.length; i++) {
if (i % 3 == 0) {
testData[i] = i; // int
} else if (i % 3 == 1) {
testData[i] = (long) i; // long
} else {
testData[i] = (double) i; // double
}
}
// 测试旧写法
long startTime = System.nanoTime();
int sum1 = 0;
for (Object value : testData) {
sum1 += processOldWay(value);
}
long oldTime = System.nanoTime() - startTime;
// 测试新写法
startTime = System.nanoTime();
int sum2 = 0;
for (Object value : testData) {
sum2 += processNewWay(value);
}
long newTime = System.nanoTime() - startTime;
System.out.println("旧写法耗时: " + oldTime / 1_000_000 + " ms");
System.out.println("新写法耗时: " + newTime / 1_000_000 + " ms");
System.out.println("性能提升: " +
String.format("%.2f%%", (1.0 - (double)newTime / oldTime) * 100));
System.out.println("结果验证: " + (sum1 == sum2 ? "通过" : "失败"));
}
}
虽然性能测试的结果会因环境而异,但新写法避免了装箱拆箱,理论上应该更快,特别是在大量数据处理的时候。
最佳实践
用原始类型模式匹配的时候,有几个最佳实践值得注意。
1. 优先使用switch表达式
switch表达式比if-else链更简洁,特别是在需要返回值的时候。
public class BestPracticeDemo {
// 推荐:使用switch表达式
public static int processValueGood(Object value) {
return switch (value) {
case int i -> i * 2;
case long l -> (int)(l / 2);
case double d -> (int) Math.round(d);
default -> 0;
};
}
// 不推荐:使用if-else链
public static int processValueBad(Object value) {
if (value instanceof int i) {
return i * 2;
} else if (value instanceof long l) {
return (int)(l / 2);
} else if (value instanceof double d) {
return (int) Math.round(d);
}
return 0;
}
}
2. 合理使用守卫模式
守卫模式能让代码更清晰,但别过度使用,否则会影响可读性。
public class GuardBestPractice {
// 推荐:合理的守卫条件
public static String categorizeGood(Object value) {
return switch (value) {
case int i when i > 0 -> "正整数";
case int i when i < 0 -> "负整数";
case int i -> "零";
case double d when d > 0 -> "正浮点数";
case double d when d < 0 -> "负浮点数";
default -> "其他";
};
}
// 不推荐:过度复杂的守卫条件
public static String categorizeBad(Object value) {
return switch (value) {
case int i when i > 0 && i < 100 && i % 2 == 0 && i % 3 == 0 -> {
// 条件太复杂,应该提取成方法
yield "复杂的条件";
}
default -> "其他";
};
}
// 推荐:复杂条件提取成方法
private static boolean isSpecialNumber(int i) {
return i > 0 && i < 100 && i % 2 == 0 && i % 3 == 0;
}
public static String categorizeBetter(Object value) {
return switch (value) {
case int i when isSpecialNumber(i) -> "特殊数字";
default -> "其他";
};
}
}
3. 处理null值
记得处理null值,避免空指针异常。
public class NullHandlingDemo {
// 推荐:明确处理null
public static String processWithNull(Object value) {
return switch (value) {
case null -> "值为null";
case int i -> "整数: " + i;
case long l -> "长整数: " + l;
default -> "其他类型";
};
}
// 不推荐:忽略null处理
public static String processWithoutNull(Object value) {
// 如果value是null,这里可能会出问题
return switch (value) {
case int i -> "整数: " + i;
case long l -> "长整数: " + l;
default -> "其他类型";
};
}
}
4. 保持代码简洁
原始类型模式匹配的优势就是简洁,别写得太复杂。
public class SimplicityDemo {
// 推荐:简洁明了
public static int doubleValue(Object value) {
return switch (value) {
case int i -> i * 2;
case long l -> (int)(l * 2);
default -> 0;
};
}
// 不推荐:过度复杂
public static int doubleValueComplex(Object value) {
if (value == null) {
return 0;
}
if (value instanceof int i) {
if (i < 0) {
return -i * 2;
} else {
return i * 2;
}
} else if (value instanceof long l) {
if (l < 0) {
return (int)(-l * 2);
} else {
return (int)(l * 2);
}
}
return 0;
}
}
常见问题和注意事项
用原始类型模式匹配的时候,可能会遇到一些问题,咱看看怎么解决。
问题1:类型擦除的影响
泛型类型擦除可能会影响模式匹配,需要注意。
import java.util.List;
public class TypeErasureDemo {
// 注意:由于类型擦除,List<Integer>和List<String>在运行时都是List
public static void processList(List<?> list) {
// 不能直接匹配List<Integer>,因为类型擦除
// case List<Integer> li -> { ... } // 编译错误
// 可以匹配List本身
if (list instanceof List) {
System.out.println("这是一个List");
}
// 对于原始类型,可以直接匹配
Object value = list.get(0);
if (value instanceof int i) {
System.out.println("第一个元素是int: " + i);
}
}
}
问题2:装箱类型的处理
原始类型模式匹配会自动处理装箱类型,但要注意边界情况。
public class BoxingDemo {
public static void processBoxed(Object value) {
// Integer会自动拆箱匹配int
if (value instanceof int i) {
System.out.println("匹配到int: " + i);
}
// 但如果是Integer对象,想获取Integer本身,需要单独处理
if (value instanceof Integer integer) {
System.out.println("Integer对象: " + integer);
// 可以获取Integer的方法
System.out.println("字节数: " + integer.byteValue());
}
// 如果想同时匹配int和Integer,需要两个case
switch (value) {
case int i -> System.out.println("原始int: " + i);
case Integer integer -> System.out.println("Integer对象: " + integer);
default -> System.out.println("其他类型");
}
}
}
问题3:switch的完整性
switch表达式必须覆盖所有可能的情况,或者有default分支。
public class CompletenessDemo {
// 编译错误:switch表达式不完整,缺少default
// public static int processIncomplete(Object value) {
// return switch (value) {
// case int i -> i * 2;
// case long l -> (int)(l / 2);
// // 缺少default,编译错误
// };
// }
// 正确:有default分支
public static int processComplete(Object value) {
return switch (value) {
case int i -> i * 2;
case long l -> (int)(l / 2);
default -> 0; // 必须有default
};
}
}
迁移指南
如果兄弟们现在用的是旧版本Java,想迁移到JDK 24用原始类型模式匹配,可以按这个步骤来。
步骤1:启用预览特性
JEP 488是预览特性,需要启用预览功能。
# 编译时启用预览
javac --enable-preview --release 24 YourClass.java
# 运行时启用预览
java --enable-preview YourClass
步骤2:逐步迁移代码
不用一次性改完,可以逐步迁移。
public class MigrationDemo {
// 旧代码:使用包装类型
public static void oldCode(Object value) {
if (value instanceof Integer) {
Integer i = (Integer) value;
System.out.println("整数: " + i);
} else if (value instanceof Long) {
Long l = (Long) value;
System.out.println("长整数: " + l);
}
}
// 新代码:使用原始类型模式匹配
public static void newCode(Object value) {
if (value instanceof int i) {
System.out.println("整数: " + i);
} else if (value instanceof long l) {
System.out.println("长整数: " + l);
}
}
// 或者用switch表达式
public static void newCodeWithSwitch(Object value) {
switch (value) {
case int i -> System.out.println("整数: " + i);
case long l -> System.out.println("长整数: " + l);
default -> System.out.println("其他类型");
}
}
}
步骤3:测试和验证
迁移后要好好测试,确保功能正常。
public class MigrationTest {
public static void testMigration(Object[] testCases) {
for (Object testCase : testCases) {
// 测试新代码
processWithPrimitivePattern(testCase);
}
}
private static void processWithPrimitivePattern(Object value) {
switch (value) {
case int i -> {
assert i >= Integer.MIN_VALUE && i <= Integer.MAX_VALUE;
System.out.println("测试通过: int " + i);
}
case long l -> {
assert l >= Long.MIN_VALUE && l <= Long.MAX_VALUE;
System.out.println("测试通过: long " + l);
}
default -> System.out.println("测试通过: 其他类型");
}
}
}
总结
JEP 488 原始类型模式匹配增强这个特性,确实让Java代码更简洁了。instanceof和switch全面支持原始类型,不用再搞那些装箱拆箱的破事,代码量少了,逻辑也更清晰。
主要优势:
- 代码更简洁:不用手动装箱拆箱,代码量减少
- 性能更好:避免不必要的对象创建,减少内存分配
- 类型安全:编译时就能检查类型,减少运行时错误
- 可读性更强:代码逻辑更清晰,维护更容易
适用场景:
- 处理JSON数据、外部API返回的数据
- 数据验证和类型转换
- 类型分发和处理
- 需要根据不同类型做不同处理的场景
注意事项:
- 这是预览特性,需要启用
--enable-preview - 记得处理null值,避免空指针异常
- switch表达式必须完整,要有default分支
- 注意类型擦除对泛型的影响
虽然还是预览特性,但已经能看到Java在朝着更现代、更简洁的方向发展。兄弟们可以在项目中试试,特别是那种需要处理多种类型数据的场景,用这个特性能省不少事。
好了,关于JEP 488原始类型模式匹配增强的内容就讲到这。下一期咱讲灵活构造函数体(JEP 492),那个特性也挺有意思的,能让构造函数写得更灵活。兄弟们有啥问题可以在评论区留言,鹏磊会尽量回复。