Java 16包括一项改进,通过JEP 395使语言更加规范,该JEP说:
内层类的静态成员
目前规定,如果一个内层类声明的成员是显性或隐性的静态成员,将是一个编译时错误,除非该成员是一个常量变量。这意味着,例如,一个内类不能声明一个记录类成员,因为嵌套的记录类是隐式静态的。
我们放宽了这个限制,以允许内层类声明显性或隐性的静态成员。特别是,这允许内层类声明一个属于记录类的静态成员。
这听起来像是为了使新特性(记录类)更加通用而做的一个小的必要的恶,实际上它有自己的生命。我们可以用它来模仿C风格的局部静态变量,即局部变量:
- 只初始化一次(而且是懒惰地初始化)
- 在一个方法的多个执行过程中共享
这听起来是一个相当棘手的功能,即只在本地可见的全局变量。但事实上,这是我长期以来一直想要的东西,特别是当我想在不污染类命名空间的情况下缓存正则表达式模式时。
考虑一下这段代码:
typescript
代码解读
复制代码
package p; import java.util.regex.Pattern; public class Test { public static void main(String[] args) { check("a"); check("b"); } static Pattern compile(String pattern) { System.out.println("compile(" + pattern + ")"); return Pattern.compile(pattern); } static void check(String string) { // Re-compiling the pattern every time: Bad // Keeping the pattern local to the method: Good System.out.println("check(" + string + "): " + compile("a").matcher(string).find()); } }
它打印出来了:
scss
代码解读
复制代码
compile(a) check(a): true compile(a) check(b): false
如果经常编译一个模式,成本会很高,所以我们最好把它缓存起来。我们过去是这样做的:
typescript
代码解读
复制代码
package p; import java.util.regex.Pattern; public class Test { public static void main(String[] args) { check("a"); check("b"); } static Pattern compile(String pattern) { System.out.println("compile(" + pattern + ")"); return Pattern.compile(pattern); } // Compiling the pattern only once: Good // Placing the pattern in a class namespace: Bad static final Pattern P_CHECK = compile("a"); static void check(String string) { System.out.println("check(" + string + "): " + P_CHECK.matcher(string).find()); } }
现在打印的是一个更理想的输出:
scss
代码解读
复制代码
compile(a) check(a): true check(b): false
也就是说,正则表达式模式只被编译一次。但不幸的是,我们不得不污染整个类的命名空间,如果我们有几十个这样的正则表达式,这很快就会变得很麻烦。我们能不能把P_CHECK
变量的范围只扩大到check()
方法?我们现在可以了!
typescript
代码解读
复制代码
package p; import java.util.regex.Pattern; public class Test { public static void main(String[] args) { check("a"); check("b"); } static Pattern compile(String pattern) { System.out.println("compile(" + pattern + ")"); return Pattern.compile(pattern); } static void check(String string) { // Compiling the pattern only once: Good // Keeping the pattern local to the method: Good // Capturing scope: Egh... var patterns = new Object() { static final Pattern P_CHECK = compile("a"); }; System.out.println("check(" + string + "): " + patterns.P_CHECK.matcher(string).find()); } }
这又一次打印出了所需的、最佳的输出:
scss
代码解读
复制代码
compile(a) check(a): true check(b): false
使用var
来使用一个不可取消的类型(其成员我们可以取消引用),再加上将静态成员放在内类中的能力,有效地模拟了局部静态变量,就像在C语言中一样。
由于内层类不太可能逃脱它的作用域,所以它可能捕获作用域的事实并不是什么大的风险,正如之前对双大括号反模式的批评中所说明的那样。你仍然在创建一个额外的类和一个无用的对象,希望逃逸分析能阻止它的分配,所以这并不是一个非常干净的解决方案,但很高兴知道现在可以这样做了。