怎么在Java 16中编写C风格的局部静态变量

2024-07-25 17:02:41 浏览数 (1)

Java 16包括一项改进,通过JEP 395使语言更加规范,该JEP说:

内层类的静态成员 目前规定,如果一个内层类声明的成员是显性或隐性的静态成员,将是一个编译时错误,除非该成员是一个常量变量。这意味着,例如,一个内类不能声明一个记录类成员,因为嵌套的记录类是隐式静态的。 我们放宽了这个限制,以允许内层类声明显性或隐性的静态成员。特别是,这允许内层类声明一个属于记录类的静态成员。

这听起来像是为了使新特性(记录类)更加通用而做的一个小的必要的恶,实际上它有自己的生命。我们可以用它来模仿C风格的局部静态变量,即局部变量:

  • 只初始化一次(而且是懒惰地初始化)
  • 在一个方法的多个执行过程中共享

这听起来是一个相当棘手的功能,即只在本地可见的全局变量。但事实上,这是我长期以来一直想要的东西,特别是当我想在不污染类命名空间的情况下缓存正则表达式模式时。

考虑一下这段代码:

代码语言:javascript复制
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());
    }
}

它打印出来了:

代码语言:javascript复制
scss 代码解读复制代码compile(a)
check(a): true
compile(a)
check(b): false

如果经常编译一个模式,成本会很高,所以我们最好把它缓存起来。我们过去是这样做的:

代码语言:javascript复制
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());
    }
}

现在打印的是一个更理想的输出:

代码语言:javascript复制
scss 代码解读复制代码compile(a)
check(a): true
check(b): false

也就是说,正则表达式模式只被编译一次。但不幸的是,我们不得不污染整个类的命名空间,如果我们有几十个这样的正则表达式,这很快就会变得很麻烦。我们能不能把P_CHECK 变量的范围只扩大到check() 方法?我们现在可以了!

代码语言:javascript复制
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());
    }
}

这又一次打印出了所需的、最佳的输出:

代码语言:javascript复制
scss 代码解读复制代码compile(a)
check(a): true
check(b): false

使用var 来使用一个不可取消的类型(其成员我们可以取消引用),再加上将静态成员放在内类中的能力,有效地模拟了局部静态变量,就像在C语言中一样。

由于内层类不太可能逃脱它的作用域,所以它可能捕获作用域的事实并不是什么大的风险,正如之前对双大括号反模式的批评中所说明的那样。你仍然在创建一个额外的类和一个无用的对象,希望逃逸分析能阻止它的分配,所以这并不是一个非常干净的解决方案,但很高兴知道现在可以这样做了。

0 人点赞