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

时间:2018-10-23 21:32:56

标签: java generics runtime-error override

在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();

似乎在重写方法时,没有检查约束。这有点奇怪,因为除非定义了通用类型(例如<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;
        }
    }
}

1 个答案:

答案 0 :(得分:0)

我看到的唯一参数是向后兼容性。例如,如果您正在使用一个返回列表的预先存在的库接口,但是您知道它仅包含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();