02、Java 22 新特性:未命名变量和模式(JEP 456)详解

兄弟们,鹏磊今天来聊聊 Java 22 里的未命名变量和模式(JEP 456)这个特性,说实话,这玩意儿挺实用的,能让你代码更简洁,可读性也更好。

这是个啥特性?

未命名变量和模式(Unnamed Variables and Patterns)是 Java 22 引入的一个新特性,用下划线 _ 来表示那些必须声明但不需要使用的变量或模式。以前写代码的时候,经常会遇到这种情况:必须声明一个变量,但实际上用不到,比如 catch 块里的异常参数、for 循环里的索引、Lambda 表达式的参数等等。现在可以用 _ 来表示,明确告诉别人"这玩意儿我不用",代码更清晰了。

为啥要引入这个特性?其实很简单,以前写代码的时候,遇到不用的变量,要么随便起个名字,要么用 IDE 的警告抑制,都不太优雅。现在用 _ 表示,一目了然,代码意图更明确,也减少了误用的可能性。

未命名变量咋用?

未命名变量主要用在几个场景:catch 子句、for 循环、try-with-resources、Lambda 表达式、赋值语句等等。咱一个个说:

catch 子句中的未命名变量

以前写 catch 块的时候,如果不需要用异常对象,也得声明个变量,现在可以用 _ 了:

// 以前得这么写,即使不用异常对象
try {
    String str = "123abc";
    int num = Integer.parseInt(str);  // 解析字符串为整数
} catch (NumberFormatException e) {  // 必须声明 e,即使不用
    System.err.println("不是数字");  // 只打印错误信息,不用异常对象
}

// 现在可以这么写,用 _ 表示不用
try {
    String str = "123abc";
    int num = Integer.parseInt(str);  // 解析字符串为整数
} catch (NumberFormatException _) {  // 用 _ 表示不用异常对象
    System.err.println("不是数字");  // 代码更简洁,意图更明确
}

这玩意儿确实方便,不用再起个没用的变量名了。而且代码意图更明确,一看就知道这个异常对象不用。

for 循环中的未命名变量

有时候循环只需要次数,不需要索引值,也可以用 _

// 循环 10 次,不需要索引
for (int _ = 0; _ < 10; _++) {  // 用 _ 表示不需要索引值
    System.out.println("Hello");  // 只关心循环次数,不关心索引
}

// 增强 for 循环,只需要遍历,不需要元素值
List<String> list = Arrays.asList("a", "b", "c");
for (String _ : list) {  // 用 _ 表示不需要元素值
    System.out.println("处理中...");  // 只关心遍历过程,不关心具体元素
}

这种场景其实挺常见的,比如初始化数组、执行固定次数的操作等等,不需要索引值的时候用 _ 更清晰。

try-with-resources 中的未命名变量

try-with-resources 语句里,如果资源对象不需要直接使用,也可以用 _

// 以前得这么写
try (var resource = acquireResource()) {  // 必须声明变量
    doSomething();  // 资源会自动关闭,但不需要直接使用
}

// 现在可以这么写
try (var _ = acquireResource()) {  // 用 _ 表示不需要直接使用资源对象
    doSomething();  // 资源会自动关闭,代码更简洁
}

这玩意儿在处理需要自动关闭但不需要直接操作的资源时特别有用,比如获取锁、打开作用域等等。

Lambda 表达式中的未命名变量

Lambda 表达式里,如果参数不用,也可以用 _

// 处理集合,不需要元素值
list.forEach(_ -> System.out.println("处理元素"));  // 用 _ 表示不需要元素值

// 二元操作,只需要一个参数
map.forEach((key, _) -> System.out.println("键: " + key));  // 只需要 key,不需要 value

这种场景在处理集合的时候挺常见的,有时候只需要遍历,不需要具体元素值。

赋值语句中的未命名变量

有时候需要调用方法获取返回值,但实际上不需要这个值,也可以用 _

// 调用方法,但不需要返回值
_ = someMethod();  // 用 _ 表示不需要返回值

// 或者更常见的场景
int _ = processData();  // 调用方法处理数据,但不需要返回值

这种场景其实不太常见,但有时候确实会遇到,比如调用有副作用的方法,只需要执行,不需要返回值。

未命名模式咋用?

未命名模式主要用在模式匹配里,特别是记录(Record)的模式匹配。Java 22 引入了记录模式匹配,可以用模式来解构记录,如果某个组件不需要,可以用 _ 表示:

// 定义一个记录
record Point(int x, int y) {}  // 点坐标记录,包含 x 和 y

// 模式匹配,只需要 x,不需要 y
static void printX(Point p) {
    if (p instanceof Point(int x, _)) {  // 用 _ 表示不需要 y 分量
        System.out.println("x = " + x);  // 只使用 x,忽略 y
    }
}

// 更复杂的例子,嵌套记录
record Rectangle(Point topLeft, Point bottomRight) {}  // 矩形记录

static void printTopLeftX(Rectangle r) {
    if (r instanceof Rectangle(Point(int x, _), _)) {  // 只需要 topLeft 的 x,其他都用 _
        System.out.println("左上角 x = " + x);  // 代码更简洁,意图更明确
    }
}

这玩意儿在处理记录的时候特别有用,可以只提取需要的组件,忽略不需要的,代码更清晰。

使用场景和最佳实践

未命名变量和模式虽然好用,但也不是啥时候都用。鹏磊总结几个使用场景:

适合用的场景

  • catch 块里不需要异常对象的时候
  • for 循环只需要次数,不需要索引的时候
  • try-with-resources 资源不需要直接使用的时候
  • Lambda 表达式参数不需要的时候
  • 记录模式匹配只需要部分组件的时候

不适合用的场景

  • 变量或模式需要被使用的时候,不能用 _
  • 需要调试的时候,_ 不能打印,不方便调试
  • 代码逻辑复杂的时候,用有意义的变量名更好理解

最佳实践

  • 只在明确不需要使用的时候用 _
  • 不要过度使用,代码可读性更重要
  • 在团队开发中,统一使用规范,避免混乱

注意事项和限制

未命名变量和模式虽然好用,但也有一些限制:

未命名变量不能读取或写入

int _ = 10;
System.out.println(_);  // 编译错误!未命名变量不能读取
_ = 20;  // 编译错误!未命名变量不能写入

这玩意儿只能声明,不能用,这是设计上的限制,避免误用。

未命名变量不会引入新的作用域名称

{
    int _ = 1;  // 第一个未命名变量
    int _ = 2;  // 第二个未命名变量,不会冲突
    // 可以在同一作用域中多次声明未命名变量
}

这玩意儿不会引入作用域名称,所以可以在同一作用域中多次声明,不会冲突。

未命名变量必须初始化

int _;  // 编译错误!未命名变量必须初始化
int _ = getValue();  // 正确,必须提供初始化

未命名变量必须在声明时初始化,不能先声明后赋值。

不能用在某些地方

// 不能用在方法参数里(这个限制可能会在后续版本放宽)
void method(int _) {  // 可能不支持,看具体版本
    // ...
}

// 不能用在字段声明里
class MyClass {
    int _;  // 不支持
}

未命名变量主要用在局部变量声明里,其他地方可能不支持,具体看版本。

实际应用案例

鹏磊给兄弟们整几个实际应用的案例,看看这玩意儿咋用:

批量处理数据

// 处理一批数据,不需要索引
List<String> data = getDataList();  // 获取数据列表
for (String _ : data) {  // 遍历数据,不需要具体元素值
    processBatch();  // 批量处理,不关心具体元素
}

异常处理

// 解析配置,不需要异常对象
try {
    int port = Integer.parseInt(config.getPort());  // 解析端口号
    startServer(port);  // 启动服务器
} catch (NumberFormatException _) {  // 端口号格式错误,不需要异常对象
    useDefaultPort();  // 使用默认端口
}

资源管理

// 获取作用域资源,不需要直接使用
try (var _ = ScopedContext.acquire()) {  // 获取作用域,不需要直接使用
    executeInScope();  // 在作用域内执行,资源会自动释放
}

记录模式匹配

// 处理用户信息,只需要部分字段
record User(String name, String email, String phone) {}  // 用户记录

void sendEmail(User user) {
    if (user instanceof User(String name, String email, _)) {  // 只需要姓名和邮箱,不需要电话
        sendEmailTo(name, email);  // 发送邮件,忽略电话
    }
}

性能影响

未命名变量和模式对性能没啥影响,就是语法糖,编译后的字节码和普通变量差不多。主要的好处是代码可读性和可维护性提升了,对性能没啥影响。

迁移建议

如果兄弟们想把旧代码迁移到使用未命名变量和模式,可以这么做:

识别可以替换的地方

  • catch 块里不用的异常参数
  • for 循环里不用的索引或元素
  • try-with-resources 里不用的资源对象
  • Lambda 表达式里不用的参数
  • 记录模式匹配里不需要的组件

逐步替换

不要一次性全替换,先从一个文件开始,看看效果,再逐步推广。

团队统一规范

在团队里统一使用规范,避免混乱,提高代码一致性。

总结

未命名变量和模式(JEP 456)这个特性,说实话,挺实用的。能让代码更简洁,可读性也更好,特别是在处理不需要使用的变量或模式的时候。虽然不是什么大特性,但确实能提升开发体验,值得用起来。

兄弟们如果还没用过,可以试试,特别是在 catch 块和 for 循环里,用 _ 替换不用的变量,代码会更清晰。好了,就这些,有啥问题欢迎反馈。

本文章最后更新于 2025-11-27