我正在寻找类似于实现 java.lang.AutoCloseable 接口(interface)的东西,其中会生成一个编译器警告,指示资源泄漏:'xxxx'从未关闭
。
其用例是 java 中同步集合的包装器。包装器有一个内部信号量来防止集合的并发修改。
它允许对集合进行原子操作,在这种情况下,信号量在内部获取和释放。它还允许从外部获取锁,从而提供可以在集合上执行操作的唯一 key 。 key 必须在“交易”结束时释放。
我的目标是在同一方法中获取锁但未释放锁时创建编译器警告,以防止死锁。防止这种情况发生的替代设计解决方案也是可以接受的。
这是一个有趣的小问题,所以我很感激任何对此的深入了解。
最佳答案
正如你所说
An alternative design solution that would prevent this is also acceptable.
所以这就是:作为替代设计解决方案,使用函数式编程。
与其找出错误,为什么不从一开始就阻止错误的发生呢?
由于缺乏您的源代码,我对您的代码做了一些假设:
-
Semaphore
是您的类(或接口(interface)),为您的SynchronizedCollection
提供信号量. -
Semaphore
提供了两种方法obtain()
和release()
.
您实际上面临的问题是国家的问题。 状态变化导致时间耦合。 obtain()
和release()
必须按顺序调用。您可以使用函数式编程中的元素作为替代设计。
Semaphore
目前看起来像这样:
public class Sempahore {
// ...
public void obtain() {
// Lock code
}
public void release() {
// Release code
}
}
Semaphore
用户当前看起来像这样:
semaphore.obtain();
// Code protected by the Sempahore.
semaphore.release();
解决方案是结合obtain()
和release()
到一个函数中,该函数将要保护的代码作为其参数。这种技术也称为“传递 block ”,或更正式地称为“高阶函数”,即采用另一个函数作为参数或返回另一个函数的函数。
Java 也有函数指针,不是直接的,而是通过接口(interface)引用间接的。从 Java 8 开始,只有一个抽象方法的接口(interface)甚至被称为函数式接口(interface),并且 Java 8 提供了可选注释 @FunctionalInterface
为此。
所以,你的class Sempahore
可以看起来像这样:
public class Semaphore {
// ...
private void obtain() {
// Lock code
}
private void release() {
// Release code
}
public <V> void protect(final Callable<V> c) throws Exception {
obtain();
try {
return c.call();
} finally {
release();
}
}
}
在 Java 7 及更早版本中,调用者看起来像这样:
semaphore.protect(new Callable<Object>() {
public Object call() {
// Code protected by the Semaphore.
}
});
在 Java 8 及更高版本中,代码也可能如下所示:
semaphore.protect(() -> {
// Code protected by the Semaphore.
});
此解决方案的怪癖
Java 有一个方面在这种情况下完全糟糕:异常处理。对于函数式编程,迫切需要解决这个问题,但 Oracle 没有。我仍然希望 Java 9,但这对所有像 java.util.stream
这样损坏的 API 没有帮助。那已经在野外了。 Java 8 仍然保留检查异常的处理或声明规则,但函数式编程并没有很好地考虑到这一点。
有一些解决方法:
- 使用
Runnable
,如果不需要返回值。 - 使用您自己的
Callable
为异常声明类型参数的接口(interface)。
我打赌使用Runnable
很简单且不言自明,因此我不会详细说明。
使用您自己的 Callable
版本界面如下所示:
public interface ProtectedCode<V,E> {
V call() throws E;
}
public class Semaphore {
// ...
private void obtain() {
// Lock code
}
private void release() {
// Release code
}
public <V, E> void protect(final ProtectedCode<V, E> c) throws E {
obtain();
try {
return c.call();
} finally {
release();
}
}
}
现在你不需要搞乱 Exception,只要类型参数 E
的类型推断有限(因为它只能反射(reflect)一种类型,而不是类型集)。在编译器中产生合理的结果。
如果您想对用户格外友好,您实际上可以提供 protect
的三种变体。方法:
-
public void protect(final Runnable r)
-
public <V> V protect(final Callable<V> c) throws Exception
-
public <V,E> V protect(final ProtectedCode<V,E> c) throws E
关于java - 如何在 java 中创建自定义编译器警告?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28949464/