【发布时间】:2019-09-12 03:15:10
【问题描述】:
我正在寻找一种“适当”的方法来减少在编译时检索/修改泛型类型参数所涉及的 Java 样板。通常,此样板文件涉及:
- 使用
@SuppressWarnings("unchecked")。 - 明确说明目标泛型类型参数。
- 通常,创建一个原本无用的局部变量,以便仅将抑制应用于该状态。
作为一个理论上的示例,假设我想保留Class 到Supplier 的映射,这样对于每个keyClass,其关联的valueSupplier 会产生扩展keyClass 的对象。
编辑 2:我将示例从Class 的映射更改为Class,将Class 的映射更改为Supplier,因为(值)Class 对象对于演员表来说是特殊的,原始示例有另一个不涉及未经检查的演员表的解决方案(感谢@Holger)。同样,我只是添加一个例子来说明问题,我不需要解决任何特定的例子。
编辑 1:更准确地说,单个 SupplierMap 对象从配置文件中填充,并保存诸如“实现接口 I1 的对象由供应商 S1 提供”、“I2 由S2”,以此类推。在运行时,我们会收到诸如I1 i1 = supplierMap.get(I1.class).get() 之类的调用,它应该生成一个带有i1.getClass() == C1.class 的对象。我对修复/快捷方式不感兴趣,例如将演员移到不属于它的位置,例如让get() 返回Supplier<Object>。演员阵容在概念上属于SupplierMap。另外,我不太关心这个具体的例子,而是关心一般的语言问题。
对于SupplierMap,我不相信有一种方法可以在Java 中捕获键值通用参数关系,从而使get() 不涉及未经检查的编译时强制转换。具体来说,我可以:
class SupplierMap {
// no way to say in Java that keys and values are related
Map<Class<?>, Supplier<?>> map;
// can check the relation at compile time for put()
<T> void put(Class<T> keyClass, Supplier<? extends T> valueSupplier) {
map.put(keyClass, valueSupplier);
}
// but not for get()
<T> Supplier<? extends T> get(Class<T> keyClass) {
@SuppressWarnings("unchecked")
final Supplier<? extends T> castValueSupplier = (Supplier<? extends T>) map.get(keyClass);
return castValueSupplier;
}
}
作为替代方案,您可以:
@SupressWarnings("unchecked")
<T> T uncheckedCast(Object o) {
return (T) o;
}
<T> Supplier<? extends T> get(Class<T> keyClass) {
return uncheckedCast(map.get(keyClass));
}
这看起来好多了,但问题是uncheckedCast 可以说是太强大了:它可能在编译时将任何东西转换为其他任何东西(通过隐藏警告)。在运行时我们仍然会得到 CCE,但这不是重点。 (论点是……)如果将这个uncheckedCast 放入库中,则该函数可能会被滥用以隐藏在编译时可检测到的问题。
有没有办法定义这样一个类似的未经检查的强制转换函数,以便编译器可以强制它只用于更改泛型类型参数?
我试过了:
// error at T<U>: T does not have type parameters
<T, U> T<U> uncheckedCast(T t) {
return (T<U>) t;
}
还有
<T, U extends T> U uncheckedCast(T t) {
return (U) t;
}
void test() {
Class<?> aClass = String.class;
// dumb thing to do, but illustrates cast error:
// type U has incompatible bounds: Class<capture of ?> and Class<Integer>
Class<Integer> iClass = uncheckedCast(aClass);
}
编辑:你有没有在公共库中看到过这种未经检查的演员表(甚至是上面的全能演员表)?我查看了 Commons Lang 和 Guava,但我唯一能找到的是 Chronicle Core 的ObjectUtils.convertTo():在那里,通过eClass == null 相当于全能的uncheckedCast,除了它还会产生一个不受欢迎的@Nullable(其他分支使用)。
【问题讨论】:
-
如果您的类型不匹配,
uncheckedCast最终只会抛出运行时 CCE。让我们退后一步:你试图用这种方法解决什么问题?当然,这不仅仅是为了好玩而玩类型标记 -
"如果将这个 uncheckedCast 放入库中,该函数很容易被滥用。"人们可以滥用更多危险的东西。如果他们这样做,这不是你的问题或责任。 Here's 我最近回答的一个相关问题,场景几乎完全相同。
-
@Rogue:我想以“适当”的方式减少样板。你是对的,功能强大的
uncheckedCast()可以工作,但具有将一些错误从编译时移到运行时的负面影响。我正在寻找一种将uncheckedCast限制为<T,U such that T == rawType(U)> U uncheckedCast(T t)之类的语言的方法,这样编译时检查就不会丢失。 -
@Kayaman 我个人同意,我只是在这方面遇到了一些困难。这似乎是一个基本的问题,我很惊讶我在一个公共库中找不到它(即使是全能的演员)。我编辑了帖子,查看了 Commons Lang、Guava 和 Chronicle Core。
-
@MateiDavid 他们不太可能将其作为通用工具暴露在外部。那些未经检查的演员表是内联完成的,并且众所周知它们是安全的。必须与类型系统作斗争应该很少见,因此拥有一些
forceCast功能是没有意义的。
标签: java generics language-lawyer compile-time