在 Java 中,通常有两种策略来管理方法所需的参数:
- 将值作为参数传递
- 将值作为类的字段
此外,为了确保线程安全,我们需要做更多的工作。对于第一种方法,问题不太明显,但对于后者,则更难处理。
确保安全的一种方法是使用 Java 的ThreadLocal,它可以确保参数不能通过不同的线程:
public class Example {
private static ThreadLocal<String> TL = new ThreadLocal<>();
public void foo() {
System.out.println(Example.get());
}
public static void main(String[] args) {
var x = new Example();
CompletableFuture.runAsync(() -> {
TL.set("^o^");
Thread.sleep(3000); // omitting exception handling
x.foo();
});
CompletableFuture.runAsync(() -> {
Thread.sleep(1000); // omitting exception handling
TL.set("o7");
x.foo();
});
}
}
|
这将打印
Project Loom 已经添加了ExtentLocal,它基本上就是一个结构化的ThreadLocal.
ThreadLocal和ExtentLocal的最有问题的是:我们失去了类型安全。对于ThreadLocal,你可以得到意想不到的空值,而对于ExtentLocal,你会得到一个异常。
任何对ThreadLocal或ExtentLocal的使用都应该附加一个null检查或绑定检查。此外,如果这两者中的一个不是私有的,就会产生耦合、安全问题和模糊的API。
另一方面,将依赖项作为参数发送还有其他问题,但我要谈的主要有两个:
- 参数膨胀
- 强制显式绑定
第一点很清楚,您可以获取具有 5/6 或更多参数的方法,这会在调用站点中创建长签名以及长签名。
第二点更容易忽略,但这里有一个例子:
public static void main(String[] args) {
foo(666)
}
public static void foo(int x) {
bar(x);
}
public static void bar(int x) {
System.out.println(x);
}
|
请注意,foo接收一个参数只是为了将它传递给bar,它实际上并没有对它做任何事情。
解决方案
这个库提供的解决方案是创建一个(部分)Coeffect System。
这个想法是使用ExtentLocal一个编译器插件来增加安全性和明确性。
Implementation note:无法创建此系统,ThreadLocal因为无法控制ThreadLocalremove.
在深入了解细节之前,让我们看看上面的例子是什么样子的:
public static void main(String[]args){
Coeffect.with(666)
.run(() -> foo());
}
@WithContext(Integer.class)
public static void foo(){
bar();
}
@WithContext(Integer.class)
public static void bar(){
System.out.println(Coeffect.get(Integer.class));
}
|
- 我们在bar中使用Coeffect.get(Integer.class)来获取存储在全局Coeffect中的顶部整数。
- 我们用@WithContext(Integer.class)对bar进行了注解,以表示我们在方法中使用Integer。
- 我们在foo中调用了bar。
- 我们用@WithContext(Integer.class)注释了foo,以表示我们正在使用一个需要Integer的方法。
- 我们调用Coeffect.with(666)将666放在Integer.class的栈顶。
- 我们在Coeffect.with(666)上调用run,在当前栈中运行一个Runnable。
- 在Coeffect.with(666).run子句中,我们正在运行foo
- 我们不需要在main方法中指定@WithContext(Integer.class),因为我们没有使用任何非绑定的依赖关系
请注意,所有这些点都是在编译时强制执行的,删除任何一个,@WithContext编译器都会对你大喊大叫。
Coeffect建立在ExtentLocal项目 Loom 附带的基础之上,以补充结构化并发,这意味着所有与线程一起工作并Coeffect一起使用的都应该使用结构化并发,任何非结构化并发的使用都可能导致误报。
名字Coeffect来自Effectsystem。Java确实有一个(部分)Effect System,checked exceptions,一个 effect 和一个 coeffect 的区别是比较细的,我希望以后给Coeffect类型系统和 Checked Exceptions 一样的力量
详细点击标题