java – 使用泛型返回类型重写抽象枚举方法时的编译错误

这是一个正确编译的代码

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