我在Java泛型中有以下场景:
public abstract class A<T> {
protected final Class<T> typeOfX;
public A(final Class<T> typeOfX) {
this.typeOfX = typeOfX;
}
public abstract void load(final T x);
}
public class AnyA<S> extends A<S> {
private final Map<String, A<? extends S>> map;
public AnyA(final Class<S> superTypeOfX,
final Map<String, A<? extends S>> map) {
super(superTypeOfX);
this.map = map;
}
@Override
public void load(final S superx) {
for (final A<? extends S> a: map.values())
if (a.typeOfX.isAssignableFrom(superx.getClass())) //Here I want to say: "if superx can be casted to a.typeOfX".
a.load(a.typeOfX.cast(superx)); //Here I want to cast superx to a.typeOfX (so as to call the load method). Here's the compile error.
}
}
我收到错误:
不兼容的类型:S无法转换为CAP#1
其中S是一个类型变量:
S扩展在AnyA类中声明的Object 其中CAP#1是一个新的类型变量:
CAP#1将S从捕获?延伸S
AnyA
是一个复合A
,即A
,可以维护其他几个A
个实例。
AnyA
方法中的 load(...)
将决定应该使用哪个维护的A
个实例来将加载过程传递给&#34;论证
换句话说,AnyA
负责找到加载参数的正确A
但AnyA
也是A
,因为它处理加载参数。
我的问题是:
当我知道S
是T
的子类并且A
中的所有AnyA
个实例都可以加载S
的子类时,为什么不能进行此转换? 1}}?
如何在不改变类图的情况下克服这个问题呢?
我已阅读有关"helper methods"的内容,但无法将此处显示的示例与我的问题相符。
我正在使用带有Java SDK 8的NetBeans IDE。
答案 0 :(得分:2)
请注意,无论您做什么,代码在任何情况下都不是“语法类型安全”。 是未经检查的演员表,唯一能防止出错的安全带是isAssignableFrom
检查。
(通常情况下,我只是提到完整性)
当您拉开线条时,错误的原因可能更明显(此处,S
代表SuperType
,根据Type Parameter Naming Conventions - 请关注它们!)
A<? extends S> a = ...;
S s = a.typeOfX.cast(s);
a.load(s);
A<? extends S>
直观地表示A
可以在load
方法中接受未知类型。您知道它扩展了S
类型,但您不知道这是哪种类型。
当您为Object
插入S
时,它可能会变得非常明显:
A<String> specificA = ...;
// So the "specificA" can load "String" objects. Then this is fine:
A<? extends Object> a = specificA;
Object s = a.typeOfX.cast(s);
// But here's the error: "s" is only an Object, and not a String!
a.load(s);
我认为混淆的主要原因(以及问题的主要原因)如下:致电
Object s = a.typeOfX.cast(s);
且typeOfX
为String.class
,则广告投放的返回类型不为String
,但仅类型此时编译器可以推断出来。在上面的示例中,这是Object
。
但是,您已经提到了Helper Methods,实际上,通过一些技巧,您可以进行编译,
import java.util.Map;
abstract class A<T>
{
protected final Class<T> typeOfX;
public A(Class<T> typeOfX)
{
this.typeOfX = typeOfX;
}
public abstract void load(T x);
}
class AnyA<S> extends A<S>
{
private final Map<String, A<? extends S>> map;
public AnyA(Class<S> superTypeOfX,
Map<String, A<? extends S>> map)
{
super(superTypeOfX);
this.map = map;
}
@Override
public void load(S s)
{
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(s.getClass()))
{
callLoad(a, s);
}
}
}
private static <S, T extends S> T cast(A<T> a, S s)
{
T t = a.typeOfX.cast(s);
return t;
}
private static <T, S extends T> void callLoad(A<S> a, T s)
{
a.load(cast(a, s));
}
}
我不会在实践中推荐这个。
个人和主观:我认为当你进行isAssignableFrom
检查时,(未经检查)的演员应该尽可能接近到这个检查。否则,代码将很难理解。
因此,虽然未经检查的强制转换在实践中是代码气味,但我尽可能避免使用SuppressWarning
,我认为这更具可读性:
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(superx.getClass()))
{
// This call is safe as of the check done above:
@SuppressWarnings("unchecked")
A<Object> castA = (A<Object>) a;
castA.load(superx);
}
}