Ответ 1
Эта сигнатура метода "полезна" в том смысле, что вы можете реализовать с ней нетривиальные, невырожденные методы (то есть возвращать null
и выбрасывать ошибки - не единственные варианты). Как показывает следующий пример, такой метод может быть полезен для реализации некоторых алгебраических структур, таких как, например, моноиды.
Во-первых, обратите внимание, что List<? extends T>
List<? extends T>
- это тип со следующими свойствами:
- Вы знаете, что все элементы этого списка соответствуют типу
T
, поэтому всякий раз, когда вы извлекаете элемент из этого списка, вы можете использовать его в позиции, где ожидаетсяT
Вы можете прочитать из этого списка. - Точный тип неизвестен, поэтому вы никогда не можете быть уверены, что экземпляр определенного подтипа
T
может быть добавлен в этот список. То есть вы фактически не можете добавлять новые элементы в такой список (если только вы не используетеnull
/type приведение/использование несостоятельности системы типов Java, то есть).
В сочетании это означает, что List<? extends T>
List<? extends T>
вроде как защищенный от добавления список с защитой от добавления уровня типа.
На самом деле вы можете делать значимые вычисления с такими "защищенными от добавления" списками. Вот несколько примеров:
-
Вы можете создавать списки с защитой от добавления одним элементом:
public static <T> List<? extends T> pure(T t) { List<T> result = new LinkedList<T>(); result.add(t); return result; }
-
Вы можете создавать списки с защитой от добавления из обычных списков:
public static <T> List<? extends T> toAppendProtected(List<T> original) { List<T> result = new LinkedList<T>(); result.addAll(original); return result; }
-
Вы можете комбинировать списки с защитой от добавления:
public static <T> List<? extends T> combineAppendProtected( List<? extends T> a, List<? extends T> b ) { List<T> result = new LinkedList<T>(); result.addAll(a); result.addAll(b); return result; }
-
И, что наиболее важно для этого вопроса, вы можете реализовать метод, который возвращает пустой список с защитой от добавления данного типа:
public static <T> List<? extends T> emptyAppendProtected() { return new LinkedList<T>(); }
Вместе они combine
и empty
форму фактической алгебраической структуры (моноида), и такие методы, как pure
гарантируют, что она не вырождена (т.е. Имеет больше элементов, чем просто пустой список). В самом деле, если у вас интерфейс, похожий на обычный Monoid класс типов:
public static interface Monoid<X> {
X empty();
X combine(X a, X b);
}
тогда вы могли бы использовать описанные выше методы для его реализации следующим образом:
public static <T> Monoid<List<? extends T>> appendProtectedListsMonoid() {
return new Monoid<List<? extends T>>() {
public List<? extends T> empty() {
return ReadOnlyLists.<T>emptyAppendProtected();
}
public List<? extends T> combine(
List<? extends T> a,
List<? extends T> b
) {
return combineAppendProtected(a, b);
}
};
}
Это показывает, что методы с сигнатурой, приведенной в вашем вопросе, могут быть использованы для реализации некоторых общих шаблонов проектирования/алгебраических структур (моноидов). Следует признать, что пример несколько надуманный, вы, вероятно, не захотите использовать его на практике, потому что вы не хотите слишком сильно удивлять пользователей своего API.
Полный скомпилированный пример:
import java.util.*;
class AppendProtectedLists {
public static <T> List<? extends T> emptyAppendProtected() {
return new LinkedList<T>();
}
public static <T> List<? extends T> combineAppendProtected(
List<? extends T> a,
List<? extends T> b
) {
List<T> result = new LinkedList<T>();
result.addAll(a);
result.addAll(b);
return result;
}
public static <T> List<? extends T> toAppendProtected(List<T> original) {
List<T> result = new LinkedList<T>();
result.addAll(original);
return result;
}
public static <T> List<? extends T> pure(T t) {
List<T> result = new LinkedList<T>();
result.add(t);
return result;
}
public static interface Monoid<X> {
X empty();
X combine(X a, X b);
}
public static <T> Monoid<List<? extends T>> appendProtectedListsMonoid() {
return new Monoid<List<? extends T>>() {
public List<? extends T> empty() {
return AppendProtectedLists.<T>emptyAppendProtected();
}
public List<? extends T> combine(
List<? extends T> a,
List<? extends T> b
) {
return combineAppendProtected(a, b);
}
};
}
public static void main(String[] args) {
Monoid<List<? extends String>> monoid = appendProtectedListsMonoid();
List<? extends String> e = monoid.empty();
// e.add("hi"); // refuses to compile, which is good: write protection!
List<? extends String> a = pure("a");
List<? extends String> b = pure("b");
List<? extends String> c = monoid.combine(e, monoid.combine(a, b));
System.out.println(c); // output: [a, b]
}
}