用“super”限制泛型关键词
为什么我只能将 super
与通配符一起使用,而不能与类型参数一起使用?
比如Collection
接口中,为什么toArray
方法不这么写
interface Collection<T>{
<S super T> S[] toArray(S[] a);
}
Why can I use super
only with wildcards and not with type parameters?
For example, in the Collection
interface, why is the toArray
method not written like this
interface Collection<T>{
<S super T> S[] toArray(S[] a);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
super
绑定命名类型参数(例如),而不是通配符(例如) 是非法,只是因为即使允许,它也不会执行您希望它执行的操作,因为
Object
是最终的super 所有引用类型,并且一切都是一个
Object
,实际上没有绑定。在您的具体示例中,由于任何引用类型的数组都是
Object[]
(通过Java数组协方差),因此它可以用作<的参数;S超级T> S[] toArray(S[] a)
(如果这样的绑定是合法的)在编译时,并且它不会在运行时阻止ArrayStoreException
。你试图提出的是给定:
并且给定这个假设
super
绑定在toArray
上:编译器应该只允许编译以下内容:
并且没有其他数组类型参数(因为
Integer
只有这 3 种类型作为super
)。也就是说,您试图阻止此编译:因为根据您的说法,
String
不是Integer
的super
。 但是,Object
是Integer
的super
,而String[]
是一个Object[]
,因此编译器仍然会让上面的代码编译,即使假设你可以!因此,以下仍然会编译(就像它们现在的样子一样),并且运行时的
ArrayStoreException
无法通过使用泛型类型的任何编译时检查来阻止边界:泛型和数组不混合,这是它显示的许多地方之一。
一个非数组示例
再次,假设您有这个泛型方法声明:
并且您有这些变量声明:
您使用
(如果合法)的意图是它应该允许add(anInteger)
和add(aNumber)
,当然还有add(anObject)
,但不是add(aString)< /代码>。好吧,
String
是一个Object
,因此add(aString)
无论如何仍然可以编译。另请参阅
相关问题
关于泛型输入规则:
列表<动物> Animals = new ArrayList()
?List
与List
在使用 < code>super 和
extends
:Java 泛型:什么是PECS?
扩展
消费者超级
”super
和extends
之间有什么区别Java泛型
和
?列表<?扩展 Number>
数据结构?(你不能!)super
to bound a named type parameter (e.g.<S super T>
) as opposed to a wildcard (e.g.<? super T>
) is ILLEGAL simply because even if it's allowed, it wouldn't do what you'd hoped it would do, because sinceObject
is the ultimatesuper
of all reference types, and everything is anObject
, in effect there is no bound.In your specific example, since any array of reference type is an
Object[]
(by Java array covariance), it can therefore be used as an argument to<S super T> S[] toArray(S[] a)
(if such bound is legal) at compile-time, and it wouldn't preventArrayStoreException
at run-time.What you're trying to propose is that given:
and given this hypothetical
super
bound ontoArray
:the compiler should only allow the following to compile:
and no other array type arguments (since
Integer
only has those 3 types assuper
). That is, you're trying to prevent this from compiling:because, by your argument,
String
is not asuper
ofInteger
. However,Object
is asuper
ofInteger
, and aString[]
is anObject[]
, so the compiler still would let the above compile, even if hypothetically you can do<S super T>
!So the following would still compile (just as the way they are right now), and
ArrayStoreException
at run-time could not be prevented by any compile-time checking using generic type bounds:Generics and arrays don't mix, and this is one of the many places where it shows.
A non-array example
Again, let's say that you have this generic method declaration:
And you have these variable declarations:
Your intention with
<T super Integer>
(if it's legal) is that it should allowadd(anInteger)
, andadd(aNumber)
, and of courseadd(anObject)
, but NOTadd(aString)
. Well,String
is anObject
, soadd(aString)
would still compile anyway.See also
Related questions
On generics typing rules:
List<Animal> animals = new ArrayList<Dog>()
?List
is different fromList<Object>
which is different from aList<?>
On using
super
andextends
:Java Generics: What is PECS?
extends
consumersuper
"super
andextends
in Java Generics<E extends Number>
and<Number>
?List<? extends Number>
data structures? (YOU CAN'T!)由于没有人提供满意的答案,正确的答案似乎是“没有充分的理由”。
Polygenelubricants 很好地概述了 java 数组协方差所发生的不良情况,这本身就是一个可怕的功能。考虑下面的代码片段:
这个明显错误的代码编译时没有诉诸任何“超级”构造,因此数组协方差不应该用作参数。
现在,我在这里有一个完全有效的代码示例,需要在命名类型参数中使用
super
:潜在地支持一些不错的用法:
如果我删除
B 总共,所以确实需要
B
。请注意,如果我反转类型参数声明的顺序,从而将
super
约束更改为extends
,那么我尝试实现的功能很容易获得。然而,只有当我将该方法重写为静态方法时,这才有可能:关键是,这种 Java 语言限制确实限制了一些原本可能有用的功能,并且可能需要丑陋的解决方法。我想知道如果我们需要
withDefault
是虚拟的,会发生什么。现在,为了与 polygenelubricants 所说的相关联,我们在这里使用
B
不是为了限制作为defaultValue
传递的对象类型(请参阅示例中使用的字符串),而是为了限制调用者对我们返回的对象的期望。作为一个简单的规则,您可以将extends
与您需要的类型一起使用,将super
与您提供的类型一起使用。As no one has provided a satisfactory answer, the correct answer seems to be "for no good reason".
polygenelubricants provided a good overview of bad things happening with the java array covariance, which is a terrible feature by itself. Consider the following code fragment:
This obviously wrong code compiles without resorting to any "super" construct, so array covariance should not be used as an argument.
Now, here I have a perfectly valid example of code requiring
super
in the named type parameter:Potentially supporting some nice usage:
The latter code fragment does not compile if I remove the
B
altogether, soB
is indeed needed.Note that the feature I'm trying to implement is easily obtained if I invert the order of type parameter declarations, thus changing the
super
constraint toextends
. However, this is only possible if I rewrite the method as a static one:The point is that this Java language restriction is indeed restricting some otherwise possible useful features and may require ugly workarounds. I wonder what would happen if we needed
withDefault
to be virtual.Now, to correlate with what polygenelubricants said, we use
B
here not to restrict the type of object passed asdefaultValue
(see the String used in the example), but rather to restrict the caller expectations about the object we return. As a simple rule, you useextends
with the types you demand andsuper
with the types you provide.您的问题的“官方”答案可以在 Sun/Oracle 错误报告。
遗憾的是,谈话到此结束。 (现已失效)链接所指向的论文是 GJ 的推断类型实例化< /a>.从最后一页来看,它可以归结为:如果允许下限,类型推断可能会产生多个解决方案,其中没有一个是 校长。
The "official" answer to your question can be found in a Sun/Oracle bug report.
Sadly, the conversation ends there. The paper to which the (now dead) link used to point is Inferred Type Instantiation for GJ. From glancing at the last page, it boils down to: If lower bounds are admitted, type inference may yield multiple solutions, none of which is principal.
唯一的原因是在类级别定义时使用 super 关键字声明类型参数是没有意义的。
Java 唯一合乎逻辑的类型擦除策略是回退到所有对象的超类型,即 Object 类。
可以在这里找到一个很好的示例和解释:
http: //www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#为什么%20is%20there%20no%20lower%20bound%20for%20type%20parameters?
类型擦除规则的简单示例可以在这里找到:
https://www.tutorialspoint.com/java_generics/java_generics_type_erasure.htm#:~:text=Type%20erasure%20is%20a%20process,there%20is%20no%20runtime%20overhead。
The only reason is it makes no sense when declaring a type parameter with a super keyword when defining at a class level.
The only logical type-erasure strategy for Java would have been to fallback to the supertype of all objects, which is the Object class.
A great example and explanation can be found here:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#Why%20is%20there%20no%20lower%20bound%20for%20type%20parameters?
A simple example for rules of type-erasure can be found here:
https://www.tutorialspoint.com/java_generics/java_generics_type_erasure.htm#:~:text=Type%20erasure%20is%20a%20process,there%20is%20no%20runtime%20overhead.
假设我们有:
基本类 A > B> C和D
<前><代码>A类{
无效方法A(){}
};
B 类扩展 A{
无效方法B(){}
}
C 类扩展 B{
void 方法C(){}
}
D 类{
无效方法D(){}
}
作业包装类
和一个管理器类,具有 4 种不同方法在对象上执行作业
和用法
现在对如何实现execute4有什么建议吗?
==========编辑=======
感谢大家:)
==========编辑==========
好多了,任何代码U 里面的execute2
超类型U 被命名!
有趣的讨论:)
Suppose we have:
basic classes A > B > C and D
job wrapper classes
and one manager class with 4 different approaches to execute job on object
with usage
Any suggestions how to implement execute4 now ?
==========edited =======
Thanks to all :)
========== edited ==========
much better, any code with U inside execute2
super type U becomes named !
interesting discussion :)
我真的很喜欢这个被接受的答案,但我想对此提出稍微不同的观点。
类型化参数中支持
super
仅是为了允许逆变功能。当谈到协方差和逆变时,了解Java仅支持使用站点方差非常重要。与 Kotlin 或 Scala 不同,它们允许声明站点差异。 Kotlin 文档此处对此进行了很好的解释。或者,如果您更喜欢 Scala,这里给你一份。它基本上意味着在 Java 中,当您根据 PECS 声明类时,您不能限制类的使用方式。该类既可以消费又可以生产,并且它的一些方法可以同时执行此操作,例如
toArray([])
顺便说一句。现在,在类和方法声明中允许使用
extends
的原因是,它更多的是关于多态性,而不是变异。一般来说,多态性是 Java 和 OOP 的固有部分:如果方法可以接受某些超类型,则始终可以安全地将子类型传递给它。如果一个方法,在声明站点因为它是“契约”,应该返回一些超类型,那么如果它在其实现中返回一个子类型,那就完全没问题了I really like the accepted answer, but I would like to put a slightly different perspective on it.
super
is supported in a typed parameter only to allow contravariance capabilities. When it comes to covariance and contravariance it's important to understand that Java only supports use-site variance. Unlike Kotlin or Scala, which allow declaration-site variance. Kotlin documentation explains it very well here. Or if you're more into Scala, here's one for you.It basically means that in Java, you can not limit the way you're gonna use your class when you declare it in terms of PECS. The class can both consume and produce, and some of its methods can do it at the same time, like
toArray([])
, by the way.Now, the reason
extends
is allowed in classes and methods declarations is because it's more about polymorphism than it is about variance. And polymorphism is an intrinsic part of Java and OOP in general: If a method can accept some supertype, a subtype can always safely be passed to it. And if a method, at declaration site as it's "contract", should return some supertype, it's totally fine if it returns a subtype instead in its implementations