问题应该是“为什么第一个可以编译”而不是“为什么第二个失败”。
两者都坏了。
像
<T> T convert(String val, Object[] aa)
方法签名说:“不管呼叫者替代T
,此方法将返回一个兼容的结果”。这不是很有用,因为唯一有效的返回值是null
,但至少,编译器会告诉您,当您尝试在以这种方式声明的方法内返回不兼容的结果时。
但子类覆盖此方法类似
Long convert(final String ion, Object[] aa)
换句话说
,覆盖,有望返回任何调用者与总是返回Long
的方法希望的方法。这首先应该感觉不对......当您返回null
时,结果仍然是兼容的,但当您返回非null
Long
值时,编译器甚至不会提醒您,因为Long
值与宣布退货类型为Long
。
但是,编译器应该已经发出了关于方法声明本身的警告。为了演示这个问题,你可以用这个声明来编写
String s = SupportedConversions.LONG.convert("bla", null);
并且编译器不会对象。如上所述,基本类型声明<T> T convert(…)
承诺返回任何呼叫者假设的T
,这里,T
已被推断为String
。这显然会在运行时中断,当实现返回一个Long
实例。
为什么这个可以编译的原因是与pre-Generics代码的兼容性。目的是让具有不同“生殖”状态的图书馆互动。例如。您可以使用最近的jdk编译Java 1.4应用程序代码,即使某些类现在覆盖 - 泛型方法。
因此,您的子类中不使用泛型的convert
方法被允许覆盖基类的convert
方法。与此相反,像
Long convert(final String ion, List<Object> aa)
方法声明是使用泛型,因此,不允许绕过通用类型系统。如果您使用原始类型List
,您又有一个非泛型声明,可绕过泛型而不受影响。
如果您现在想说,假设这里的泛型行为是不合逻辑的,那么您并不孤单。不仅因为覆盖方法与覆盖的通用声明位于相同的编译单元(enum
声明)之内,两者都在enum
声明内,这是一种在Java 5之前不存在的语法结构(引入泛型)。
此外,最重要的方法利用协变返回类型,Long
RESP。 Integer
其中删除方法声明的返回类型为Object
,这也不能出现在Java 5之前的代码中。
但是这些(仍然)是规则。您应该在这里关注编译器警告。如果您没有收到警告(我知道Netbeans IDE的默认设置不严格),您应该尝试启用它们。
这段代码没有修正。 enum
s不可能做什么。您可以删除类型参数T
并让基类型的方法声明返回Object
,但enum
常量中的协变返回类型是不相关的,因为它们不属于public
API的一部分。最好的选择是:
public interface SupportedConversions<T> {
SupportedConversions<Integer> INTEGER = (String ion, Object[] aa) -> {
return null;
};
SupportedConversions<Long> LONG = (String ion, Object[] aa) -> {
return null;
};
public abstract T convert(String val, Object[] aa);
}
resp。
public interface SupportedConversions<T> {
SupportedConversions<Integer> INTEGER = (ion, aa) -> {
return null;
};
SupportedConversions<Long> LONG = (ion, aa) -> {
return null;
};
public abstract T convert(String val, List<Object> aa);
// we can support both variants
public default T convert(String val, Object[] aa) {
return convert(val, Arrays.asList(aa));
}
}
这很奇怪,虽然我确定有一个完全合乎逻辑的理由。如果你从'List'中移除类型参数,那么编译器错误就会消失。或者,您可以将类型参数设置为“T”。数组的版本会发出如下警告:“未检查重写:返回类型需要未经检查的转换。找到'java.lang.Long',必需'T'” – Jeremy
是的编译错误消失,如果我删除显式类型的列表压倒一切的方法。 – Sparky