java - 将 Java 构造函数注释为实现 @FunctionalInterface
问题描述
我想使用 Spring 的这个功能接口:
@FunctionalInterface
public interface RowMapper<T> {
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
这是一种通过显式声明将只调用构造函数的 RowMapper 常量来使用它的方法:
namedParameterJdbcTemplate.queryForObject(sql, parameters, ValueObject.ROW_MAPPER);
public class ValueObject {
public static final RowMapper<ValueObject> ROW_MAPPER = (resultSet, rowNum) -> new ValueObject(resultSet);
public final long field;
public ValueObject(ResultSet resultSet) throws SQLException {
field = resultSet.getLong("FIELD");
}
}
你看:我不使用 rowNum 参数。
我想要一个更简洁和富有表现力的代码。我想直接使用构造函数而不必声明 RowMapper:
namedParameterJdbcTemplate.queryForObject(sql, parameters, ValueObject::new);
public class ValueObject {
public final long field;
public ValueObject(ResultSet resultSet, int unusedRowNumFromRowMapperInterface) throws SQLException {
field = resultSet.getLong("FIELD");
}
}
干净多了,但 IDE 和 Sonar 现在抱怨未使用的参数。
我可以将 @SuppressWarnings({"unused", "java:S1172"}) 添加到该参数。但这会污染干净的解决方案:我不希望项目中的其他开发人员盲目地为他们创建的每个 ValueObject 复制/粘贴这个 vaudou 咒语。而且我也不希望他们创建一个常量+一个构造函数样板。
有没有办法通知编译器构造函数实际上正在实现 RowMapper @FunctionalInterface,以便它知道第二个参数是必需的,即使未使用?
或者另一种不太直接的方式来干净地摆脱警告?
我尝试创建此注释以使用有意义的名称注释未使用的参数,封装删除警告的实现细节,但这也不起作用:
@Target(ElementType.PARAMETER)
@Inherited
@SuppressWarnings({"unused", "java:S1172"})
public @interface ThisParameterIsFromRowMapperInterface {
}
解决方案
将 Java 构造函数注释为实现 @FunctionalInterface
这就像问“为绿色添加一个角”。这毫无意义。
FunctionalInterface 的要点是将接口标记为定义“函数”,在某种意义上,您可以在需要该接口的值的地方编写 lambda 语法( (a, b) -> result
)或方法引用构造( ),然后 javac 将foo::bar
自动为您修复问题,使其正常工作。
您不会注释适合该模式并且可以用作方法引用的方法(或构造函数)。
干净多了,但 IDE 和 Sonar 现在抱怨未使用的参数。
你听过关于医生的比喻吗?
一位病人问医生:“医生,我按这里就疼!”
医生说:“好吧。好吧,那就别这样了!”
问题在于您的 IDE/Sonar,而不是您的代码。关闭“检查器”/“linter”功能,这很愚蠢,默认情况下是关闭的,所以有人打开它,认为(错误地)这是一个有用的检查。
此检查有一个正确的版本:当且仅当 linter 工具具有整个方法层次结构的完整视图(因此不仅是方法本身,而且覆盖它的所有方法,该方法覆盖的所有方法,以及所有external-to-this-codebase 方法,将来可能会覆盖它),并且所有这些方法的参数都会被忽略,那么就可以发出警告。
鉴于 linter 没有水晶球,首先要确保方法是有效final
或有效(包)私有的,以便将不可知的外部集合减少到 0。检查不能适用于任何非最终公开的任何东西:也许该参数的存在是为了覆盖该方法的代码。(想一想:当你有一个abstract
方法时,所有参数都被“忽略”,因为根本没有代码!)
如果启用了更智能的拍摄,这里就不会出现警告:根据定义,lambdas 会覆盖某些东西。
有没有办法通知编译器构造函数实际上正在实现 RowMapper @FunctionalInterface,以便它知道第二个参数是必需的,即使未使用?
那没有。您可以创建第二个构造函数,它采用第二个参数(类型int
),只是为了完全忽略此参数,但如果它是智能 linting,实际上会触发该 linter 警告,因为根据定义,构造函数不能覆盖某些东西,也不能被覆盖,因此有资格进行“忽略参数”检查是有用的,这不是很讽刺吗?
我强烈建议不要创建一个有效地需要以下 javadoc 的构造函数:
/**
* This constructor completely ignores the second parameter.
* It is intended to be used in the form of `MyType::new`,
* when you need a `RowMapper`.
* <strong>NB: Any other use is neccessarily a bug</strong>.
*/
因为,好吧,读它。使用粗体警告来解释事物的预期用途令人惊讶是不好的:您不希望代码库中出现意外,也不希望在不阅读文档的情况下可能会被误解的方法。
我试图创建这个注解来用有意义的名称来注解未使用的参数,封装删除警告的实现细节,但这也不起作用
那是行不通的;注释不会像这样“元”。您可以对注释定义进行注释,但这并不意味着“使用此注释注释事物意味着使用所有这些注释对事物进行注释”。这可能意味着,但前提是注释(和相关的工具!)被定义为这样工作,因为它不是内置在 java 中的。@SuppressWarnings
不能那样工作。
好的,那么在这种情况下我该怎么办?
我建议你尝试这样的事情:
public class WhateverYouHaveThere {
public static RowMapper asRowMapper() {
return (rs, idx) -> new WhateverYouHaveThere(rs);
}
}
这个方法不需要javadoc也不需要注释:方法名+static
修饰符100%覆盖了它的目的,实现一点也不奇怪。
然后,当然,修复你的 linter 中的愚蠢设置 :) - 它会警告这个方法的事实足以证明它只是妨碍你,而不是让你对可疑代码有任何有意义的见解。
然后它让你写:
public static final RowMapper<ValueObject> ROW_MAPPER = ValueObject.asRowMapper();
注意:您也可以将其设为静态字段:
public class ValueObject {
public static final RowMapper<ValueObject> ROW_MAPPER =
(rs, idx) -> new ValueObject(rs);
}
但我不会。更多的是一种风格。许多支持 getter 而非公共 final 字段的论点也适用于此。基本上:在将来给自己更多的灵活性来添加日志记录、更改实现等。
推荐阅读
- google-chrome - VueJS Chrome 页面无响应
- javascript - Chrome 扩展 - 未捕获的 DOMException:阻止具有来源的框架访问跨域框架
- javascript - 缩放 div 内容以适应固定大小
- python - 如何使用 setuptools 将 css 文件与单个模块烧瓶应用程序打包?
- java - 错误:java.util.concurrent.ExecutionException:com.android.builder.internal.aapt.v2.Aapt2Exception:AAPT2 错误:查看日志了解详细信息
- csv - 使用 apache camel 从 FTP 服务器下载 csv
- .net-core - .NET Core 中的窗口消息循环(WndProc | While (GetMEssage))线程
- ruby-on-rails-5 - 跨多个表连接(HABTM、Rails、Ruby)
- c++ - 重载运算符=? C++
- vba - 对于每个编译错误(多个范围)