<T>列表<吗?扩展 T> f() 一个有用的签名
是有用的签名吗?它/使用它有任何问题吗?<T> List<? extends T> f()
这是一个面试问题。我知道这个:
- 编译精细
- 像 这样使用它,然后我只能调用那些签名中不包含 T 的 List 方法(例如,isEmpty()、size(),但不能添加(T)、remove(T)
List<? extends Number> lst = obj.<Number>f()
这是否完全回答了这个问题?
是有用的签名吗?它/使用它有任何问题吗?<T> List<? extends T> f()
这是一个面试问题。我知道这个:
List<? extends Number> lst = obj.<Number>f()
这是否完全回答了这个问题?
此方法签名是“有用的”,从某种意义上说,您可以使用它实现非平凡的、非退化的方法(也就是说,返回和抛出错误不是您唯一的选择)。如下面的示例所示,这样的方法对于实现某些代数结构(例如幺半群)非常有用。null
首先,观察这是具有以下属性的类型:List<? extends T>
T
T
T
null
结合使用,这意味着它类似于追加保护列表,具有类型级追加保护。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>();
}
一起形成一个实际的代数结构(一个幺半群),以及诸如确保它是非退化的(即它有更多的元素只是一个空列表)之类的方法。实际上,如果你有一个类似于通常的Monoid typeclass的接口:combine
empty
pure
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]
}
}
我将“它是否是一个有用的签名”解释为“你能想到它的用例吗”。
T
是在调用站点确定的,而不是在方法内部确定的,因此只能从该方法返回两个内容:null 或空列表。
鉴于您可以在与调用此方法大致相同的代码中创建这两个值,因此没有充分的理由使用它。
实际上,可以安全返回的另一个值是所有元素都为 null 的列表。但这也没有用,因为您只能调用从返回值添加或删除文本 null 的方法,因为 in 类型绑定。所以你所拥有的只是计算它所包含的空值数量的东西。这也无用。? extends