考虑类Foo
的以下构造函数(为了清楚起见,不是泛型类):
public <T> Foo(T obj) { }
这是构造函数的有效语法,就像普通generic methods一样。
但这种语法有什么用?通常,泛型方法为其返回类型提供类型安全性,并且可以受益于编译器的类型推断。例如:
Pair<String, Integer> stringInt = Pair.of("asfd", 1234);
但是对构造函数的调用总是返回其声明类的实例,因此其类型参数对返回类型没有影响。上面的构造函数可以替换为erasure:
public Foo(Object obj) { }
当然,泛型不仅仅是关于返回类型的类型安全性。构造函数可能只想约束传入的参数类型。但是,上述推理仍然适用于有界类型参数:
public <N extends Number> Foo(N number) { }
public Foo(Number number) { } //same thing
甚至带有边界的嵌套类型参数也使用通配符处理:
public <N extends Number, L extends List<N>> Foo(L numList) { }
public Foo(List<? extends Number> numList) { } //same thing
那么拥有通用构造函数的合法用例是什么?
答案 0 :(得分:6)
这是一个可能的功能编程改编版。假设我们有一个Stream
类型,它有一些内部状态,在返回null
之前反复产生新元素。外部调用者不关心流类型的内部状态类型,因此您可能会得到类似
class Stream<E> {
<S> Stream(S initialState, StepFunction<E, S> stepFun) {
...
}
}
没有收件人必须知道内部状态类型是什么。
答案 1 :(得分:5)
我能想到的一件事就是你可以确保在多个参数中以相同的方式实现边界。
采取一个明显愚蠢且设计但有效的构造函数,将列表从源复制到目标:
public <T> Foo (List<T> listA, List<T> listB) {
listA.addAll(listB);
}
在这里使用通配符会很快变得非常讨厌,可能无论如何都不会做你想要的。不允许它也是完全武断的限制。因此,语言规范允许它对我有意义。
答案 2 :(得分:2)
我能想到的一个用例是当你想要将构造函数参数约束到多个类型时。只有通用语法允许您声明构造函数采用List
Number
个RandomAccess
来实现public <L extends List<? extends Number> & RandomAccess> Foo(L raNumList) { }
...
Foo f1 = new Foo(new ArrayList<Integer>());
Foo f2 = new Foo(new LinkedList<Integer>()); //compiler error
:
{{1}}
答案 3 :(得分:1)
您可以对构造函数参数强制执行某些约束。例如。以下代码需要两个实现接口InterfaceA和InterfaceB的参数。
<T extends InterfaceA & InterfaceB > Foo(T t1, T t2) {
}
答案 4 :(得分:1)
主要用途是确保多个参数之间满足类型约束。这是一个以正确的顺序将一堆组件放在装配线上的示例:
public <T> AssemblyLine(T[] starting, List<T> components) {
T[] a = components.toArray(starting);
Arrays.sort(a);
this.conveyorBelt.add(a);
}
此处<T>
确保T[]
和List<T>
保持相同类型T
,而不是(比方说),Integer[]
和List<string>