如何解决Java运行时转换为通用接口而不会丢失类型信息
我有一个通用接口,可以由抽象基类的不同子类实现。
interface PropertyOne<T> {
T type();
List<String> values();
}
在基类中,我定义了一个小的辅助方法,该方法检查当前实例是否实现了该接口并在可能的情况下强制转换为该接口:
<T> Optional<T> toProperty(Class<T> clazz){
if (clazz.isAssignableFrom(this.getClass())){
return Optional.of(clazz.cast(this));
}
return Optional.empty();
}
例如,我使用此辅助方法来获取和打印这些值:
void printValues() {
List<String> values = toProperty(PropertyOne.class)
.map(p -> p.values())
.orElse(Collections.emptyList());
System.out.println(values);
}
但这给了我一个不安全的强制警告,因为values()
现在返回List
而不是List<String>
。我知道,通用类型信息在编译代码后会丢失,但是由于values
的返回类型始终是List
类型的String
,因此仍然应该安全吗?
使用以下样板代码无需警告即可工作。
if ( this instanceof PropertyOne<?>){
List<String> vs = ((PropertyOne<?>)this).values();
System.out.println(vs);
}
我可以像第.map(p -> ((PropertyOne<?>)p).values())
一样手动转换第一个变体,以消除警告,但这又增加了很多样板。
所以我有两个问题:
- 为什么
List<String>
类型的信息首先丢失? - 是否可以直接动态地转换为通配符/无界类型,而无需随后手动转换为
PropertyOne<?>
?
解决方法
因为Class<T>
上的泛型非常有限并且不能以这种方式使用(我想说j.l.Class
上的泛型已损坏,但这可能有点苛刻。它们也有一些用途,只是..不多)。泛型可以表示类实例无法做到的事情;类实例(java.lang.Class
的实例可以表示泛型不能的事物。具体地说,List<String>
不能表示为类实例,而int.class
是不能用泛型表示的类实例。
换句话说,当您尝试在PropertyOne<String>
实例中传达完整的泛型(例如java.lang.Class<T>
)时,您就输了游戏-j.l.Class
中的T只能永远都是非泛型类型。
所以,这不是怎么做。
您的toProperty
方法必须执行。不针对本文中的问题;这是关于尝试在您的PropertyOne实例上调用type()
的情况,该实例是从(可选包装)toProperty调用派生的。
与此不同的是,您似乎在values()
调用中遇到麻烦(似乎没有实际相关的泛型部分)的原因是由于过时的Java规则:一旦“原始” ,一切都是原始的,甚至不需要的东西。
因此,在toProperty(PropertyOne.class)
中,最终将返回Optional<PropertyOne>
-原始类型,因为这样做不可行;您需要例如PropertyOne<?>
与PropertyOne
不同(最后一个是原始的,前一个不是原始的)-但是您无法使用Class<T>
来这样做,因为Class<T>
已损坏。现在您处于原始区域,并且在此原始类型上调用values()
最终将返回原始列表(仅List
),您认为仍然可以给您一个List<String>
作为values()
的返回类型不依赖于PropertyOne
的泛型,但这不是它的工作方式,java lang spec表示不。
有很多方法可以解决这个难题,但是大多数情况下,您只是在滥用泛型,您实际上无法做到这一点。记住,泛型是编译器想象力的伪装:它们在运行时根本不存在。无法在运行时检查任何内容。一旦开始混合使用泛型和运行时结构(例如instanceof
或x.isAssignableFrom
),就永远无法解决问题。
如果必须的话,您可以使用名为“超级类型令牌”的东西到达那里-您可以在Google上搜索(将“ java”作为关键字折腾),这些可以精确地代表您可以粘贴在泛型中的任何内容。但这听起来还是一个错误。但我不能真正告诉您您在这里必须做什么,您的问题描述是在谈论一个坏的解决方案,而不是您的坏解决方案正在尝试解决的问题。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。