根据 Java编程语言第4版。第15.7.1节“类型代币”:
getClass 会受到编译器的特殊处理:如果在静态类型为 T 的引用上调用 getClass ,则编译器将 getClass 的返回类型视为 Class 。所以这有效:
String str = "Hello";
Class<? extends String> c2 = str.getClass(); // compiler magic
javadocs for the getClass
method in class Object
提供更多细节:
[
getClass()
]的实际结果类型为Class<? extends |X|>
,其中|X|
是擦除调用getClass的表达式的静态类型。例如,此代码片段中不需要强制转换:Number n = 0; Class<? extends Number> c = n.getClass();
这是Java和类getClass()
的{{1}}方法。将注意力转移到Scala,SLS 3.2.10读取,
存在类型的占位符语法
语法:
Object
Scala支持存在类型的占位符语法。 通配符类型属于 表单 _&gt;:L&lt;:U ...通配符类型是存在的简写 量化类型变量,其中存在量化是隐含的。
...让 T = pc [targs,T,targs'] 为参数化类型,其中 targs , targs'可能是empty和 T 是通配符类型 _&gt;:L&lt;:U 。然后 T 等同于存在类型
pc [targs,t,targs] forSome { type t&gt;:L&lt;:U }
其中 t 是一些新的类型变量。
我强调“T等同于存在主义类型......”因为我观察到的行为似乎与该陈述不一致。
在Scala repl中,我尝试了SLS 3.2.10的通配符语法:
WildcardType ::= ‘_’ TypeBounds
这符合我的预期。但是,如果我依赖于SLS 3.2.10中声称的等效性“通配符类型是存在量化类型变量的简写”,我会意外地失败。
scala> val c: Class[_ >: scala.Nothing <: String] = "foo".getClass
c: Class[_ <: String] = class java.lang.String
错误消息似乎正在引导我递归回到SLS 3.2.10,建议我同时使用通配符语法和快速存在量化。我不明白这意味着什么。无论如何,我使用上面引用的scala> val c: Class[t forSome { type t >: scala.Nothing <: String }] = "foo".getClass
<console>:7: error: type mismatch;
found : java.lang.Class[?0] where type ?0 <: java.lang.String
required: Class[t forSome { type t <: String }]
Note: ?0 <: t forSome { type t <: String }, but Java-defined class Class is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: t forSome { type t <: String }`. (SLS 3.2.10)
val c: Class[t forSome { type t >: scala.Nothing <: String }] = "foo".getClass
^
javadoc中的示例观察到相同的二分法:
Object
使用:
scala> val n: Number = 0
n: java.lang.Number = 0
不起作用:
scala> val c: Class[_ >: scala.Nothing <: Number] = n.getClass
c: Class[_ <: java.lang.Number] = class java.lang.Integer
如果特定的通配符类型与特定的存在类型“等效”,那是否意味着可以替换另一个?这不是等价的意思吗?假设我正确理解SLS 3.2.10中使用的“等价”含义,我是否在根据SLS 3.2.10中规定的规则进行等效替换时出错?如何处理我在上面引用的包含与SLS 3.2.10一致的存在类型的两个语句的失败,根据这些语句,失败的语句等同于使用成功的通配符类型的语句?
错误消息的必需和找到行中指定的类型之间有什么区别?也就是说,这是怎么回事:
scala> val c: Class[t forSome { type t >: scala.Nothing <: Number }] = n.getClass
<console>:8: error: type mismatch;
found : java.lang.Class[?0] where type ?0 <: java.lang.Number
required: Class[t forSome { type t <: java.lang.Number }]
Note: ?0 <: t forSome { type t <: java.lang.Number }, but Java-defined class Class is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: t forSome { type t <: java.lang.Number }`. (SLS 3.2.10)
val c: Class[t forSome { type t >: scala.Nothing <: Number }] = n.getClass
与
不同java.lang.Class[?0] where type ?0 <: java.lang.String
第一个问题是什么?显然Class[t forSome { type t <: String }]
意味着什么,它似乎是一个类型变量,但使用像这样的问号不是Scala,是吗?它是什么语言,在哪里指定,以便我能理解错误信息?
答案 0 :(得分:5)
再次查看您引用的规范部分:
设T = p.c [targs,T,targs']是参数化类型,其中targs,targs'可以为空,T是通配符类型_&gt;:L&lt;:U。然后T相当于存在类型 p.c [targs,t,targs] forSome {type t&gt;:L&lt;:U} 其中t是一些新的变量。
请注意,它会显示p.c[targs, t , targs ] forSome { type t >: L <: U }
和不 p.c[targs, t forSome { type t >: L <: U } , targs ]
。这两种类型是不同的。
回到Class
的上下文,这意味着Class[_ >: scala.Nothing <: String]
相当于Class[t] forSome { type t >: scala.Nothing <: String}
,不 Class[t forSome { type t >: scala.Nothing <: String }]
。
果然,如果你在REPL中输入以下内容,那就很好了:
val c: Class[t] forSome { type t >: scala.Nothing <: String } = "foo".getClass
答案 1 :(得分:0)
我不知道Scala,但是根据我对Java的通配符的理解,我猜错了。
如果表达式的类型包含通配符,则java会对其执行“通配符捕获”
expr: "foo".getClass()
type: Class<? extends String>
after wildcard capture
type: Class<X>, for some X that X<:String
这两种类型并不完全相同,第二种类型更具体(这是首先进行通配符捕获的原因,以便我们获得更多关于表达式的类型信息)
“通配符捕获”广泛应用于任何表达式。因此,在几乎所有情况下,包含通配符的类型可以说等同于捕获转换后的类型。例如,表达式"foo".getClass()
的类型为Class<? extends String>
,但我们可以将其类型视为Class<X>, for some X that X<:String
但这两种类型并不总是可以交换的。在Java中,第二种类型不可表示;假设它是,意味着我们可以声明类似
的类型Class<Y>(for some Y<:String) var = "foo".getClass();
该行不应该编译,因为在捕获转换后,右侧会获得一个不同的,不相关的类型变量,比如X.因此该行尝试分配Class<X> to Class<Y>
。
这有效
Class<? extends String> var = "foo".getClass();
因为Class<X>(where X<:String)
是Class<? extends String>
的子类型。事实上,
for any X<:String, Class<X> <: Class<? extends String>
可以被认为是通配类型的定义,它本质上是一个联合。
o instanceof Class<? extends String>
iff
there exists X<:String and o instanceof Class<X>
(Java类型系统不考虑这样的事实,String
是最终的,所以所有这些类型几乎相同,因为它们都包含相同的元素String.class)
当我们谈论表达类型时,我们可以说这两种类型是等价的;当我们谈论变量类型时,它们并不等同。