首页 > 解决方案 > 为什么在实现(覆盖)具有此类约束的方法时,Java 允许忽略/删除通用约束?

问题描述

在实现(覆盖)具有此类约束的方法时,它允许忽略/删除通用约束是 Java 中的一个限制吗?

或者,它是否按预期工作,这是滥用的一个例子,而不是预期会导致问题的问题?

我可以编写一个具有通用约束的接口:

public interface X
{
    public <T extends Number> List<T> get();
}

然后我可以在忽略约束的接口中实现这样的:

public static class BadX implements X
{
    @Override
    public List<String> get()
    {
        return Arrays.asList("a", "b", "c");
    }
}

现在,当使用这样的实现时,我可以分配一个错误的类型,而不会出现任何编译时或运行时错误:

X x = new BadX();

// wooops, it's actually a list of strings,
// but no compile-time nor runtime errors
List<Double> doubles = x.get();

似乎在覆盖该方法时,未检查约束。这有点奇怪,因为 List 返回类型是不允许的,除非定义了泛型类型(例如<T>,或<T extends OtherType>)。

下面的完整代码示例:

import java.util.Arrays;
import java.util.List;


public class JavaFiddle
{
    public static void main(String[] args)
    {
        System.out.println("STARTED");

        NumberSource ns = new IntegerSource(Arrays.asList(1, 2, 3));

        // wooops, it's actually a list of integers,
        // but no compile-time nor runtime errors
        List<Double> doubles = ns.getSource();

        // works
        printNumbers(doubles);

        // runtime error
        printDoubles(doubles);

        System.out.println("DONE");
    }

    public static void printNumbers(List<? extends Number> numbers)
    {
        for (Number n : numbers)
            System.out.println(n);
    }

    public static void printDoubles(List<Double> doubles)
    {
        for (Double d : doubles)
            System.out.println(d);
    }


    public interface NumberSource
    {
        public <T extends Number> List<T> getSource();
    }


    public static class IntegerSource implements NumberSource
    {
        private List<Integer> source;

        public IntegerSource(List<Integer> integers)
        {
            this.source = integers;
        }

        @Override
        public List<Integer> getSource()
        {
            return source;
        }
    }
}

标签: javagenericsruntime-erroroverriding

解决方案


我看到的唯一论点是向后兼容性。例如,如果您正在使用返回 List 的预先存在的库接口,但您知道它只包含 Integer 对象,则可以强制转换它:

    List<Integer> myList = legacyObject.legacyGetList(); // returns a List

请参阅本文档的第 6.1 节: https ://www.oracle.com/technetwork/java/javase/generics-tutorial-159168.pdf

您可能已经在考虑这一点,但在您的情况下,将 NumberSource 实现为泛型类并使用类似的东西可能更安全/更易于维护

    NumberSource<Integer> 

IntegerSource 将扩展 NumberSource。这将允许如下代码:

    NumberSource<Integer> ns = new IntegerSource(Arrays.asList(1, 2, 3));

    // This no longer compiles
    List<Double> doubles = ns.getSource();

推荐阅读