Подпись этого метода «полезна» , в том смысле, что вы можете реализовать с ней нетривиальные, невырожденные методы (то есть возврат null
и выдача ошибок - не единственные варианты).Как показывает следующий пример, такой метод может быть полезен для реализации некоторых алгебраических структур, таких как, например, моноиды.
Во-первых, обратите внимание, что List<? extends T>
- это тип со следующими свойствами:
- Вы знаете, что все элементы этого списка соответствуют типу
T
, поэтому всякий раз, когда вы извлекаете элемент из этого списка, вы можете использовать его в позиции, где ожидается T
.Вы можете прочитать из этого списка . - Точный тип неизвестен, поэтому вы никогда не можете быть уверены, что экземпляр определенного подтипа
T
может быть добавлен в этот список.То есть вы фактически не можете добавлять новые элементы в такой список (если только вы не используете null
s / type приведение / использование ненадежности системы типов Java, то есть).
В совокупности это означает, что 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
образуют фактическую алгебраическую структуру (amonoid) и такие методы, как 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]
}
}