Java 将泛型与'绑定;超级';关键词
为什么我只能将Java 将泛型与'绑定;超级';关键词,java,generics,language-design,super,Java,Generics,Language Design,Super,为什么我只能将super与通配符一起使用,而不能与类型参数一起使用 例如,在Collection界面中,为什么toArray方法不是这样编写的 interface Collection<T>{ <S super T> S[] toArray(S[] a); } 接口集合{ S[]到阵列(S[]a); } super绑定命名类型参数(例如)而不是通配符(例如),因为没有人提供满意的答案,正确的答案似乎是“没有充分的理由” PultEnguluBrimeCutes
super
与通配符一起使用,而不能与类型参数一起使用
例如,在Collection
界面中,为什么toArray
方法不是这样编写的
interface Collection<T>{
<S super T> S[] toArray(S[] a);
}
接口集合{
S[]到阵列(S[]a);
}
super
绑定命名类型参数(例如
)而不是通配符(例如),因为没有人提供满意的答案,正确的答案似乎是“没有充分的理由”
PultEnguluBrimeCutes提供了一个很好的综述,它是Java数组协方差的坏事情,这本身就是一个可怕的特性。
String[] strings = new String[1];
Object[] objects = strings;
objects[0] = 0;
这显然是错误的代码在编译时没有使用任何“超级”构造,因此数组协方差不应用作参数
现在,我有一个非常有效的示例,说明在命名类型参数中需要super
:
class Nullable<A> {
private A value;
// Does not compile!!
public <B super A> B withDefault(B defaultValue) {
return value == null ? defaultValue : value;
}
}
关键是,这种Java语言限制确实限制了一些其他可能有用的功能,可能需要难看的解决方法。我想知道如果我们需要withDefault
虚拟化会发生什么
现在,为了与Polygene所说的内容相关联,我们在这里使用B
,而不是限制作为defaultValue
传递的对象的类型(参见示例中使用的字符串),而是限制调用者对我们返回的对象的期望。作为一个简单的规则,您可以使用extends
和所需的类型,使用super
和所提供的类型。假设我们有:
- 基本等级A>B>C和D
class A{
void methodA(){}
};
class B extends A{
void methodB(){}
}
class C extends B{
void methodC(){}
}
class D {
void methodD(){}
}
- 作业包装器类
interface Job<T> {
void exec(T t);
}
class JobOnA implements Job<A>{
@Override
public void exec(A a) {
a.methodA();
}
}
class JobOnB implements Job<B>{
@Override
public void exec(B b) {
b.methodB();
}
}
class JobOnC implements Job<C>{
@Override
public void exec(C c) {
c.methodC();
}
}
class JobOnD implements Job<D>{
@Override
public void exec(D d) {
d.methodD();
}
}
接口作业{
无效执行官(T);
}
类JobOnA实现作业{
@凌驾
公共行政主任(A){
a、 方法a();
}
}
类JobOnB实现作业{
@凌驾
公开作废执行官(B){
b、 方法b();
}
}
类JobOnC实现作业{
@凌驾
公开作废执行官(C){
c、 方法c();
}
}
类JobOnD实现作业{
@凌驾
公共行政主任(D){
d、 方法d();
}
}
- 和一个管理器类,具有4种不同的方法来执行对象上的作业
class Manager<T>{
final T t;
Manager(T t){
this.t=t;
}
public void execute1(Job<T> job){
job.exec(t);
}
public <U> void execute2(Job<U> job){
U u= (U) t; //not safe
job.exec(u);
}
public <U extends T> void execute3(Job<U> job){
U u= (U) t; //not safe
job.exec(u);
}
//desired feature, not compiled for now
public <U super T> void execute4(Job<U> job){
U u= (U) t; //safe
job.exec(u);
}
}
类管理器{
最终T;
经理(T){
t=t;
}
public void execute1(作业作业){
执行主任(t);
}
public void execute2(作业作业){
U=(U)t;//不安全
行政总裁(u);
}
public void execute3(作业){
U=(U)t;//不安全
行政总裁(u);
}
//所需功能,目前未编译
public void execute4(作业作业){
U=(U)t;//安全
行政总裁(u);
}
}
- 使用
void usage(){
B b = new B();
Manager<B> managerB = new Manager<>(b);
//TOO STRICT
managerB.execute1(new JobOnA());
managerB.execute1(new JobOnB()); //compiled
managerB.execute1(new JobOnC());
managerB.execute1(new JobOnD());
//TOO MUCH FREEDOM
managerB.execute2(new JobOnA()); //compiled
managerB.execute2(new JobOnB()); //compiled
managerB.execute2(new JobOnC()); //compiled !!
managerB.execute2(new JobOnD()); //compiled !!
//NOT ADEQUATE RESTRICTIONS
managerB.execute3(new JobOnA());
managerB.execute3(new JobOnB()); //compiled
managerB.execute3(new JobOnC()); //compiled !!
managerB.execute3(new JobOnD());
//SHOULD BE
managerB.execute4(new JobOnA()); //compiled
managerB.execute4(new JobOnB()); //compiled
managerB.execute4(new JobOnC());
managerB.execute4(new JobOnD());
}
void用法(){
B=新的B();
经理b=新经理(b);
//太严格
managerB.execute1(newjobona());
managerB.execute1(new JobOnB());//已编译
managerB.execute1(newjobonc());
managerB.execute1(newjobond());
//太多的自由
managerB.execute2(new JobOnA());//已编译
managerB.execute2(new JobOnB());//已编译
managerB.execute2(new JobOnC());//已编译!!
managerB.execute2(new JobOnD());//已编译!!
//限制不充分
managerB.execute3(newjobona());
managerB.execute3(new JobOnB());//已编译
managerB.execute3(newjobonc());//已编译!!
managerB.execute3(newjobond());
//应该是
managerB.execute4(new JobOnA());//已编译
managerB.execute4(new JobOnB());//已编译
managerB.execute4(newjobonc());
managerB.execute4(newjobond());
}
现在有没有关于如何实现execute4的建议
=============已编辑=======
public void execute4(Job<? super T> job){
job.exec( t);
}
public void execute4(Job您的问题的“官方”答案可以在中找到
BT2:评估
看
特别是第3节和第9页的最后一段。承认
子类型约束两侧的类型变量可能导致
没有单一最佳解的类型方程组;因此,
无法使用任何现有标准进行类型推断
这就是类型变量只有“扩展”边界的原因
另一方面,通配符不必进行推断,因此
不需要这个约束
@###.### 2004-05-25
是的;关键是,即使捕获到通配符,也只使用通配符
作为推理过程的输入;没有(只有)下限需求
作为结果被推断
@###.### 2004-05-26
我明白问题所在,但我不明白它与问题有什么不同
在推理过程中,我们有通配符的下界,例如:
列表我真的很喜欢被接受的答案,但我想对它提出一个稍微不同的观点
super
仅在类型化参数中支持逆变功能。当涉及到协方差和逆变时,必须了解Java仅支持使用站点方差。与Kotlin或Scala不同,后者允许声明站点方差riance。Kotlin文档对此进行了很好的解释。或者,如果您更喜欢Scala,那么这是一个适合您的文档
这基本上意味着在Java中,当你用PEC声明类时,你不能限制你使用它的方式。这个类既可以使用也可以生成,它的一些方法可以同时使用,顺便说一下,比如toArray([])
现在,类和方法声明中允许使用扩展
的原因是因为它更多地是关于多态性而不是关于差异。而多态性通常是Java和OOP的固有部分:如果一个方法可以接受某个超类型,那么子类型总是可以安全地传递给如果是一个方法,在声明站点
String[] strings = new String[1];
Object[] objects = strings;
objects[0] = 0;
class Nullable<A> {
private A value;
// Does not compile!!
public <B super A> B withDefault(B defaultValue) {
return value == null ? defaultValue : value;
}
}
Nullable<Integer> intOrNull = ...;
Integer i = intOrNull.withDefault(8);
Number n = intOrNull.withDefault(3.5);
Object o = intOrNull.withDefault("What's so bad about a String here?");
// This one actually works and I use it.
public static <B, A extends B> B withDefault(Nullable<A> nullable, B defaultValue) { ... }
class A{
void methodA(){}
};
class B extends A{
void methodB(){}
}
class C extends B{
void methodC(){}
}
class D {
void methodD(){}
}
interface Job<T> {
void exec(T t);
}
class JobOnA implements Job<A>{
@Override
public void exec(A a) {
a.methodA();
}
}
class JobOnB implements Job<B>{
@Override
public void exec(B b) {
b.methodB();
}
}
class JobOnC implements Job<C>{
@Override
public void exec(C c) {
c.methodC();
}
}
class JobOnD implements Job<D>{
@Override
public void exec(D d) {
d.methodD();
}
}
class Manager<T>{
final T t;
Manager(T t){
this.t=t;
}
public void execute1(Job<T> job){
job.exec(t);
}
public <U> void execute2(Job<U> job){
U u= (U) t; //not safe
job.exec(u);
}
public <U extends T> void execute3(Job<U> job){
U u= (U) t; //not safe
job.exec(u);
}
//desired feature, not compiled for now
public <U super T> void execute4(Job<U> job){
U u= (U) t; //safe
job.exec(u);
}
}
void usage(){
B b = new B();
Manager<B> managerB = new Manager<>(b);
//TOO STRICT
managerB.execute1(new JobOnA());
managerB.execute1(new JobOnB()); //compiled
managerB.execute1(new JobOnC());
managerB.execute1(new JobOnD());
//TOO MUCH FREEDOM
managerB.execute2(new JobOnA()); //compiled
managerB.execute2(new JobOnB()); //compiled
managerB.execute2(new JobOnC()); //compiled !!
managerB.execute2(new JobOnD()); //compiled !!
//NOT ADEQUATE RESTRICTIONS
managerB.execute3(new JobOnA());
managerB.execute3(new JobOnB()); //compiled
managerB.execute3(new JobOnC()); //compiled !!
managerB.execute3(new JobOnD());
//SHOULD BE
managerB.execute4(new JobOnA()); //compiled
managerB.execute4(new JobOnB()); //compiled
managerB.execute4(new JobOnC());
managerB.execute4(new JobOnD());
}
public void execute4(Job<? super T> job){
job.exec( t);
}
private <U> void execute2(Job<U> job){
U u= (U) t; //now it's safe
job.exec(u);
}
public void execute4(Job<? super T> job){
execute2(job);
}