这是一个正确编译的代码
public enum SupportedConversions {
INTEGER {
@Override
public Integer convert( String ion, Object[] aa) {
return null;
}
},
LONG {
@Override
public Long convert(final String ion, Object[] aa) {
return null;
}
};
public abstract <T> T convert(String val, Object[] aa);
}
但是当我将抽象函数参数更改为对象列表而不是数组时,我得到编译错误,说“方法不会覆盖超类”.并且仅当返回类型是通用的时才会发生这种情况
示例坏代码
public enum SupportedConversions {
INTEGER {
@Override
public Integer convert( String ion, List<Object> aa) {
return null;
}
},
LONG {
@Override
public Long convert(final String ion, List<Object> aa) {
return null;
}
};
public abstract <T> T convert(String val, List<Object> aa);
}
有没有理由不这样做.看起来更像是Java中的错误
最佳答案 问题应该是“为什么第一次被编译”而不是“为什么第二次失败”.
两者都破了.
方法签名就像
<T> T convert(String val, Object[] aa)
说“无论调用者如何替代T,这种方法都会返回兼容的结果”.这不是很有用,因为唯一有效的返回值是null,但至少,当您尝试在以此方式声明的方法中返回不兼容的结果时,编译器会告诉您.
但是子类重写了这个方法
Long convert(final String ion, Object[] aa)
换句话说,覆盖一个方法,该方法承诺返回调用者希望的方法,并始终返回Long.首先应该感觉不对…当你返回null时结果仍然是兼容的,但是当你返回一个非空的Long值并且编译器甚至不会警告你时,结果仍然是兼容的,因为Long值与声明返回类型为Long.
但是,编译器应该已经发出了关于方法声明本身的警告.为了证明问题,使用该声明,您可以编写
String s = SupportedConversions.LONG.convert("bla", null);
并且编译器不会反对.如上所述,基本类型声明< T> T convert(…)promises返回调用者为T假设的任何内容,在这里,T被推断为String.当实现返回Long实例时,这显然会在运行时中断.
这可以编译的原因是与前Generics代码的兼容性.目的是允许具有不同“泛化”状态的图书馆进行互动.例如.您可以使用最近的jdk编译Java 1.4应用程序代码,即使某些类覆盖了现在的Generic方法.
因此,允许子类中不使用泛型的convert方法覆盖基类的convert方法.相比之下,方法声明就像
Long convert(final String ion, List<Object> aa)
正在使用泛型,因此,不允许绕过泛型类型系统.如果您使用原始类型List,那么您将再次获得一个非泛型声明,它可以绕过泛型.
如果你现在想说,在这里假设前泛型行为是不合逻辑的,你并不孤单.不仅因为重写方法在被覆盖的Generic声明的同一编译单元(枚举声明)内,它们都在枚举声明中,这是一个在Java 5之前不存在的语法结构(引入了泛型).
此外,重写方法使用协变返回类型,Long resp.方法声明的擦除具有返回类型Object的整数,它也不能出现在Java 5之前的代码中.
但这些(仍然)是规则.您应该关注编译器警告.如果你没有收到警告(我知道Netbeans IDE有默认的麻烦),你应该尝试启用它们.
此代码没有修复.你想要做什么,用枚举是不可能的.您可以删除类型参数T并让基类型的方法声明返回Object,但枚举常量中的协变返回类型是无关紧要的,因为它们不是公共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));
}
}