Java Generic Interface vs Generic Methods, и когда использовать один

мне было интересно, помимо синтаксической разницы, когда можно использовать общий интерфейс над методом, который принимает общий параметр?

public interface Flight<T>{
   void fly(T obj);
}

над

public interface Flight{
    void <T> fly(T obj);
}

3 ответов


если вы объявите общий метод, вы всегда позволяете абонента решите, какие аргументы использовать для параметров типа. Реализация метода должна иметь возможность обрабатывать все возможные аргументы типов (и у нее даже нет способа запросить фактические аргументы типа).

тем не менее, такой метод, как <T> void fly(T obj); указано, что абонент может использовать любой тип для T пока единственное, на что может положиться реализация, это то, что фактический тип для T будет назначить Object (как если <T extends Object> была объявлена).

таким образом, в этом конкретном примере это не отличается от объявления void fly(Object obj);, что также позволяет произвольным объектам.

напротив, параметр типа на interface является частью договора и может быть указан или ограничен реализация на interface:

public interface Flight<T>{
   void fly(T obj);
}

позволяет реализациям как

public class X implements Flight<String> {
   public void fly(String obj) {
   }
}

фиксация типа T на стороне реализации. Или

public class NumberFlight<N extends Number> implements Flight<N> {
   public void fly(N obj) {
   }
}

будучи по-прежнему общим, но ограничения тип.


подпись interface также важно, когда interface сам является частью другой сигнатуры метода, например

public void foo(Flight<? super String> f) {
    f.fly("some string value");
}

здесь Flight реализация, которую вы переходите к foo, должен быть способен потреблять String значение, поэтому Flight<String> или Flight<CharSequence> или Flight<Object> достаточно, но не Flight<Integer>. Для объявления такого контракта требуются параметры типа на interface, не на interface’ы методов.


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

например, ArrayList<E> является универсальным типом, так как большинство его операций (add, get, remove и т. д.) полагаться на тип, указанный при создании одного.

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

вы можете прочитать больше о дженерики в Java Docs.


возьмите, например, класс java.util.ArrayList<E>. При создании переменных этого типа необходимо указать конкретный тип для T:

ArrayList<String> list = new ArrayList<>();

эти конкретные типы используются при вызове методов из List интерфейс, который работает с типом T. Вызываю add метод, вы можете только добавить String объекты в список. Извлечение элементов из списка с помощью get, вы получите элементы конкретного типа String.

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

MyObject obj = SomeClass.staticGenericMethod(MyObject.class)

или

MyObject obj = classInstance.genericMethod(MyObject.class);

и вы должны начать свое имя интерфейса с заглавной буквы:Flight<T>