使用泛型返回类型覆盖抽象枚举方法时的编译错误

时间:2017-03-05 06:46:13

标签: java generics enums java-8

这是一个正确编译的代码

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中的错误

1 个答案:

答案 0 :(得分:1)

问题应该是“为什么第一次编译”而不是“为什么第二次失败”。
两者都坏了。

这样的方法签名
<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实例时,这显然会在运行时中断。

可以编译它的原因是与前Generics代码的兼容性。目的是允许具有不同“泛化”状态的图书馆进行互动。例如。您可以使用最近的jdk编译Java 1.4应用程序代码,即使某些类覆盖了现在的Generic方法。

因此,允许子类中不使用Generics的convert方法覆盖基类的convert方法。相比之下,像

这样的方法声明
Long convert(final String ion, List<Object> aa)

使用泛型,因此,不允许绕过Generic类型系统。如果您使用原始类型List,那么您将再次获得一个非泛型声明,该声明可以绕过泛型。

如果你现在想要说,假设前泛化行为在这里是不合逻辑的,那么你并不孤单。不仅因为重写方法在被覆盖的Generic声明的同一编译单元(enum声明)内,它们都在enum声明中,这是一个在Java之前不存在的语法结构5(引入泛型的地方)。

此外,重写方法使用协变返回类型Long resp。 Integer,其中方法声明的擦除返回类型为Object,它也不能出现在Java 5之前的代码中。

但这些(仍然)是规则。您应该关注编译器警告。如果你没有收到警告(我知道Netbeans IDE有默认的麻烦),你应该尝试启用它们。

此代码无法修复。 enum s无法实现您的目标。您可以删除类型参数T并让基类型的方法声明返回Object,但enum常量中的协变返回类型是无关紧要的,因为它们不是{{1}的一部分API。最好的选择是:

public

RESP。

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);
}