Подтвердить что ты не робот

Родовые типы подстановок не должны использоваться в возвращаемых параметрах

Можно ли сказать, что общие шаблонные типы не должны использоваться в возвращаемых параметрах метода?

Другими словами, имеет смысл объявить интерфейс следующим образом:

interface Foo<T> {
  Collection<? extends T> next();
}

Кроме того, можно ли утверждать, что общие шаблонные типы имеют смысл только при объявлении параметра

4b9b3361

Ответ 1

Основное преимущество использования подстановочных типов, скажем, в формальном параметре метода, заключается в предоставлении гибкости пользователю для передачи, например любого типа Collection или List или всего, что реализует Collection (при условии, что коллекция объявлен как Collection<?>). Вы часто оказываетесь с использованием типов подстановочных знаков в формальных параметрах.

Но в идеале вам следует избегать использования их в качестве возвращаемого типа вашего метода. Таким образом, вы заставите пользователя этого метода использовать типы подстановочных знаков в конце вызова, даже если они этого не хотят. Используя подстановочные типы, вы говорите это, эй! этот метод может возвращать любой тип Collection, так что ваша задача позаботиться об этом. Вы не должны этого делать. Лучше использовать параметр ограниченного типа. С параметром ограниченного типа тип будет выводиться на основе типа, который вы передаете, или целевого типа вызова метода.

И вот цитата из Эффективного Java Item 28:

Не используйте типы подстановочных знаков в качестве возвращаемых типов. Вместо того, чтобы предоставлять дополнительная гибкость для ваших пользователей, это заставит их использовать типы подстановочных знаков в клиентском коде.
Правильно используемые типы подстановочных знаков почти невидимый для пользователей класса. Они вызывают методы принятия параметры, которые они должны принимать и отвергать те, которые они должны отклонить. Если пользователь класса должен думать о типах подстановок, есть вероятно, что-то не так с API классов.

Ответ 2

Нет, это невозможно сделать.

Или сказать так: имеет смысл иметь такой интерфейс.

Представьте себе следующее

interface Foo<T>  
{
    Collection<? extends T> next();
}

class FooInteger implements Foo<Number> 
{
    private final List<Integer> integers = new ArrayList<Integer>();
    void useInternally()
    {
        integers.add(123);
        Integer i = integers.get(0);
    }

    @Override
    public Collection<? extends Number> next() 
    { 
        return integers;
    }
}

// Using it:
Foo<Number> foo = new FooInteger();
Collection<? extends Number> next = foo.next();
Number n = next.iterator().next();

Если вы написали тип возврата как Collection<T>, вы не смогли бы вернуть коллекцию, содержащую подтип T.

Независимо от того, желательно ли иметь такой тип возврата, зависит от случая приложения. В некоторых случаях это может быть просто необходимо. Но если этого легко избежать, тогда вы можете это сделать.


EDIT: Отредактируйте код, чтобы указать на разницу, а именно, что вы не всегда сможете выбирать тип внутри. Однако в большинстве случаев можно избежать чего-то, что связано с шаблоном может, и, как я сказал, по возможности, его следует избегать.

Приведенный выше пример следует по-прежнему рассматривать как пример, чтобы подчеркнуть ключевой момент. Хотя, конечно, такая реализация будет плохой практикой, поскольку она подвергает внутреннее состояние.

В этом и подобных случаях часто можно возвращать что-то вроде

return Collections.<Number>unmodifiableList(integers);

и тем самым объявить возвращаемый тип как Colletion<Number>: метод unmodifiableList решает проблему открытого внутреннего состояния и обладает опрятным свойством, которое позволяет изменять параметр типа на супертип, поскольку список то... ну, немодифицируемый так или иначе.

Ответ 3

Чтобы избежать предупреждений (пример: Sonar), замените Class<?> на Class<? extends Object> или вообще не используйте его. Просто верните class.

Ответ 4

Настоятельно рекомендуется не использовать подстановочные типы в качестве возвращаемых типов. Поскольку правила вывода типов довольно сложны, вряд ли пользователь этого API будет знать, как его правильно использовать. Давайте возьмем пример метода, возвращающего "Список". Можно ли в этот список добавить пса, кота... мы просто не знаем. И компилятор также не делает, поэтому он не допускает такого прямого использования. Использование групповых типов должно быть ограничено параметрами метода.

Это правило поднимает проблему, когда метод возвращает тип шаблона.

Пример несовместимого кода

  List<? extends Animal> getAnimals(){...}  

Совместимое решение

  List<Animal> getAnimals(){...} 

или же

  List<Dog> getAnimals(){...}

РЕДАКТИРОВАТЬ

ИСТОЧНИК - https://rules.sonarsource.com/java/RSPEC-1452