本地类改进 - Java 细品

在最近发布的 Java 版本中,对定义本地类和概念的能力进行了多项改进。这些更改中的大多数来自在实现其他功能时的清理工作。将一个概念的定义和作用域限制在它相关的范围内可以提高可读性并防止引入错误。如果使用不当,它们也可能成为错误的来源。让我们看看三种允许开发人员在方法内定义类或概念的改进。

不可标记类型

局部变量类型推断,var,是在 JDK 10 中引入的。 var 可以通过在定义局部变量时节省时间来改善使用局部变量的体验。不太为人知的是,随着 var 的引入,保留不可标记类型的类型信息的能力。让我们看看下面的例子

public void printNamesNicely() {
    var data = """
               Billy,Korando
               """;
    try (var scanner = new Scanner(data)) {
        while (scanner.hasNextLine()) {
            var line = scanner.nextLine().split(",");
            var person = new Object() {
                String fName = line[0];
                String lName = line[1];
                void prettyPrint() {
                    System.out.println("%s, %s".formatted(fName, lName));
                }
            };
        person.prettyPrint();
        }
    }
}                                              

这里使用 var 捕获匿名类的类型信息,特别是 prettyPrint()。使用 Object 作为变量 person 的类型意味着 prettyPrint() 将丢失,并且在行 person.prettyPrint(); 上会出现编译器错误。使用 var 保留匿名类的类型信息,允许在方法 printNamesNicely() 中稍后引用 prettyPrint()var 保留类型信息的能力在处理捕获类型和交集类型时也很有用。

扩展的本地类类型

在 JDK 16 之前,只能在方法体中定义一个 class。随着本地记录的添加,此限制被解除,因此其他 class 类型,abstractinterfaceenum,也可以在方法体中定义。在下面的示例中,enum SizesizeFinder(String) 的方法体中定义

public String sizeFinder(String mySize) {
	enum Size {
		SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
		private String shortSize;
		private Size(String shortSize) {
			this.shortSize = shortSize;
		}

		public static Size lookupByShortSize(String shortSize) {
			for (int i = 0; i < Size.values().length; i++) {
				if (shortSize.equals(Size.values()[i].shortSize)) {
					return Size.values()[i];
				}
			}
			throw new IllegalArgumentException("Size not found!");
		}
	}
	return Size.lookupByShortSize(mySize).name();
} 

enum SizeString 值的含义提供了额外的上下文;SMLXL。如果这些值仅在 sizeFinder(String) 中使用,那么在 sizeFinder(String) 中定义 enum Size 可以限制维护应用程序的开发人员的认知负担。开发人员只需要在与方法 sizeFinder(String) 交互时才关心这些信息。

内部类和本地类的静态成员

JDK 16 还取消了内部类和本地类不能有静态成员的限制。这使得在调用之间维护方法局部状态成为可能,如下面的示例所示

public void recordUsers(String user) {
	class Holder {
		static List<String> userRecord = new ArrayList<>();
	}
	Holder.userRecord.add(user);
	System.out.println("User: " + user + " interaction has been recorded");
	System.out.println("All users:" + Holder.userRecord.toString());
}

本地类 Holder 包含 static 成员 List<String> userRecord。每次调用 recordUsers(String) 时,传入的参数都会持久化到 List<String> userRecord 中。

能力越大,责任越大

这些更改可以提高 Java 应用程序的可表达性,或者如果需要,可以将概念定义与它在应用程序中的使用位置更紧密地耦合。这些更改也可能被滥用,从而破坏可读性,导致线程安全问题或其他潜在问题。

在考虑使用这些功能时,请权衡潜在的风险和收益。

其他阅读材料

祝您编码愉快!