如何在 java 中创建自定义编译器警告?
How can I create custom compiler warnings in java?
我正在寻找类似于实现 java.lang.AutoCloseable 接口的东西,其中会生成指示 Resource leak: 'xxxx' is never closed
的编译器警告。
此用例位于 java 中同步集合的包装器中。包装器有一个内部信号量来防止集合的并发修改。
它允许对集合进行原子操作,在这种情况下,信号量是在内部获取和释放的。它还允许从外部获取锁,提供唯一的密钥,使用该密钥可以在集合上执行操作。密钥必须在 "transaction".
结束时释放
我的目标是在同一方法中获取锁但未释放锁时创建编译器警告,以防止死锁。可以防止这种情况的替代设计解决方案也是可以接受的。
这是一个有趣的小问题,所以我很感激任何对它的见解。
为了创建编译器警告,您需要扩展 Eclipse 编译器。
另一种解决方案是在软件质量分析系统中创建自定义检查,例如 Teamscale 或 SonarQube。 自定义检查执行代码的静态分析(通常基于富含语义信息的抽象语法树)并在检测到不可靠代码时产生问题。这些问题显示在质量分析系统的用户界面上。 Eclipse 插件允许在 Eclipse 中集成系统,以便也可以在其中列出问题。
如你所说
An alternative design solution that would prevent this is also acceptable.
所以这里是:作为替代设计解决方案,使用函数式编程。
与其找出错误,不如首先阻止错误的发生?
由于缺少您的源代码,我对您的代码做出一些假设:
Semaphore
是您的 class(或接口),它为您的 SynchronizedCollection
. 提供信号量
Semaphore
提供了两种方法obtain()
和release()
.
您实际面临的问题是 State 的问题。 状态改变 导致时间耦合。 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()
组合成一个函数,该函数将要保护的代码作为其参数。这种技术也称为传递块,或更正式地称为高阶函数 - 将另一个函数作为参数或return是另一个函数。
Java 也有函数指针,不是直接的,而是通过对接口的引用间接的。从Java8开始,只有一个抽象方法的接口甚至被称为Functional Interface,而Java8提供了一个optional[=103] =] 注释 @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,但这对所有损坏的 API 都无济于事,例如 java.util.stream
已经在野外了。 Java 8 仍然保持处理或声明-检查异常的规则,但是函数式编程并没有很好地考虑到这一点。
有一些解决方法:
- 如果不需要 return 个值,请使用
Runnable
。
- 使用您自己的
Callable
接口,为异常声明类型参数。
我敢打赌使用 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();
}
}
}
现在,只要类型参数 E
的有限(因为它只能反映一种类型,而不是类型集)类型推断导致合理的结果,您就不需要乱用 Exception编译器。
如果您想对用户特别友好,实际上可以提供 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
虽然@Christian Hujer 确实提供了可靠的解决方案,但我选择了另一条行之有效的路线。
SynchronizedCollection 周围有一个包装器 class "Resource",其中包含:
- 用于锁定集合的信号量
- 一个随机生成的 ID,表示当前持有的锁的密钥
- 对集合执行原子操作的方法(获取锁,执行操作,立即释放)
- 对集合执行非原子操作的方法(它们接受一个 ID 作为键,如果提供的键与当前持有锁的键匹配,则执行请求的操作)
上面描述的 class 足以为集合提供足够的保护,但我想要的是如果锁未释放则编译器警告。
为了实现这一点,有一个 "ResourceManager" 实现了 java.lang.AutoCloseable
这个class:
- 通过构造函数
传递了对"Resource"的引用
- 获取构造函数中引用的锁
- 提供一个 API 用于调用 "Resource" 上的非原子方法,使用它在构建期间获得的密钥
- 提供一个 close() 方法,重写 java.lang.AutoCloseable,释放在构建过程中获得的锁
资源管理器是在需要对资源执行多个操作的任何地方创建的,如果未在任何特定代码路径上调用 close(),则会生成编译器警告。此外,在 java 7+ 中,可以在 try-with-resource 块中创建管理器,并且无论块中发生什么,锁都会自动释放。
我正在寻找类似于实现 java.lang.AutoCloseable 接口的东西,其中会生成指示 Resource leak: 'xxxx' is never closed
的编译器警告。
此用例位于 java 中同步集合的包装器中。包装器有一个内部信号量来防止集合的并发修改。
它允许对集合进行原子操作,在这种情况下,信号量是在内部获取和释放的。它还允许从外部获取锁,提供唯一的密钥,使用该密钥可以在集合上执行操作。密钥必须在 "transaction".
结束时释放我的目标是在同一方法中获取锁但未释放锁时创建编译器警告,以防止死锁。可以防止这种情况的替代设计解决方案也是可以接受的。
这是一个有趣的小问题,所以我很感激任何对它的见解。
为了创建编译器警告,您需要扩展 Eclipse 编译器。
另一种解决方案是在软件质量分析系统中创建自定义检查,例如 Teamscale 或 SonarQube。 自定义检查执行代码的静态分析(通常基于富含语义信息的抽象语法树)并在检测到不可靠代码时产生问题。这些问题显示在质量分析系统的用户界面上。 Eclipse 插件允许在 Eclipse 中集成系统,以便也可以在其中列出问题。
如你所说
An alternative design solution that would prevent this is also acceptable.
所以这里是:作为替代设计解决方案,使用函数式编程。
与其找出错误,不如首先阻止错误的发生?
由于缺少您的源代码,我对您的代码做出一些假设:
Semaphore
是您的 class(或接口),它为您的SynchronizedCollection
. 提供信号量
Semaphore
提供了两种方法obtain()
和release()
.
您实际面临的问题是 State 的问题。 状态改变 导致时间耦合。 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()
组合成一个函数,该函数将要保护的代码作为其参数。这种技术也称为传递块,或更正式地称为高阶函数 - 将另一个函数作为参数或return是另一个函数。
Java 也有函数指针,不是直接的,而是通过对接口的引用间接的。从Java8开始,只有一个抽象方法的接口甚至被称为Functional Interface,而Java8提供了一个optional[=103] =] 注释 @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,但这对所有损坏的 API 都无济于事,例如 java.util.stream
已经在野外了。 Java 8 仍然保持处理或声明-检查异常的规则,但是函数式编程并没有很好地考虑到这一点。
有一些解决方法:
- 如果不需要 return 个值,请使用
Runnable
。 - 使用您自己的
Callable
接口,为异常声明类型参数。
我敢打赌使用 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();
}
}
}
现在,只要类型参数 E
的有限(因为它只能反映一种类型,而不是类型集)类型推断导致合理的结果,您就不需要乱用 Exception编译器。
如果您想对用户特别友好,实际上可以提供 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
虽然@Christian Hujer 确实提供了可靠的解决方案,但我选择了另一条行之有效的路线。
SynchronizedCollection 周围有一个包装器 class "Resource",其中包含:
- 用于锁定集合的信号量
- 一个随机生成的 ID,表示当前持有的锁的密钥
- 对集合执行原子操作的方法(获取锁,执行操作,立即释放)
- 对集合执行非原子操作的方法(它们接受一个 ID 作为键,如果提供的键与当前持有锁的键匹配,则执行请求的操作)
上面描述的 class 足以为集合提供足够的保护,但我想要的是如果锁未释放则编译器警告。
为了实现这一点,有一个 "ResourceManager" 实现了 java.lang.AutoCloseable
这个class:
- 通过构造函数 传递了对"Resource"的引用
- 获取构造函数中引用的锁
- 提供一个 API 用于调用 "Resource" 上的非原子方法,使用它在构建期间获得的密钥
- 提供一个 close() 方法,重写 java.lang.AutoCloseable,释放在构建过程中获得的锁
资源管理器是在需要对资源执行多个操作的任何地方创建的,如果未在任何特定代码路径上调用 close(),则会生成编译器警告。此外,在 java 7+ 中,可以在 try-with-resource 块中创建管理器,并且无论块中发生什么,锁都会自动释放。