Почему Java позволяет игнорировать / отбрасывать общие ограничения при реализации (переопределении) метода с такими ограничениями? - PullRequest
0 голосов
/ 24 октября 2018

Является ли ограничение в 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;
        }
    }
}

1 Ответ

0 голосов
/ 24 октября 2018

Единственный аргумент, который я видел для этого, - обратная совместимость.Например, если вы используете ранее существующий интерфейс библиотеки, который возвращает список, но вы знаете, что он содержит только объекты 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...