2

根据Java 编程语言第 4 版。第 15.7.1 节“类型令牌”:

编译器对getClass进行特殊处理:如果在静态类型T的引用上调用getClass,则编译器将getClass的返回类型视为Class。所以这有效:

String str = "Hello";
Class<? extends String> c2 = str.getClass(); // compiler magic

类中方法javadocs 提供了getClassObject更多详细信息:

实际结果类型 [of getClass()]是Class<? extends |X|>调用|X|getClass 的表达式的静态类型的擦除。例如,此代码片段中不需要强制转换:

Number n = 0; 
Class<? extends Number> c = n.getClass();

那是 Java 和getClass()class 的方法Object。将注意力转移到 Scala,SLS 3.2.10 读取,

存在类型的占位符语法

句法:

WildcardType ::= ‘_’ TypeBounds

Scala 支持存在类型的占位符语法。通配符类型的形式为_ >: L <: U ...通配符类型是存在量化类型变量的简写,其中存在量化是隐含的。

...令T = pc[targs, T, targs']为参数化类型,其中targs , targs'可能为空,并且T是通配符类型_ >: L <: U。那么T等价于存在类型

pc[targs, t, targs] forSome { type t >: L <: U }

其中t是一些新的类型变量。

我在上面强调了“T 等同于存在类型......”,因为我观察到的行为似乎与该陈述不一致。

我做了什么

在 Scala repl 中,我尝试了 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
                                                                         ^

错误消息似乎将我递归地引导回 SLS 3.2.10,建议我同时使用通配符语法和表达存在量化。我不明白那是什么意思。无论如何,我使用Object上面引用的 javadocs 中的示例观察到相同的二分法:

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

不工作:

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

问题

主要是

如果特定的通配符类型与特定的存在类型“等效”,这是否意味着可以用一个替换另一个?这不就是等价的意思吗?假设我正确理解了 SLS 3.2.10 中使用的“等价”的含义,我尝试根据 SLS 3.2.10 中规定的规则进行等效替换是否有错误?repl 如何处理我上面引用的包含与 SLS 3.2.10 一致的存在类型的两个语句的失败,根据这些语句,失败的语句等同于使用成功的通配符类型的语句?

此外

相关错误消息的required行和found行中指定的类型有什么区别?即,这是怎么回事:

java.lang.Class[?0] where type ?0 <: java.lang.String

不同于

Class[t forSome { type t <: String }]

第一个问号是什么?显然?0意味着什么,它似乎是一个类型变量,但使用这样的问号不是Scala,是吗?那是什么语言,在哪里指定,以便我可以理解该错误消息?

4

2 回答 2

5

再次查看您引用的规范部分:

令 T = pc[targs, T, targs'] 为参数化类型,其中 targs, targs' 可能为空,而 T 为通配符类型 _ >: L <: U 。那么 T 等价于存在类型 pc[targs, t , targs ] forSome { type t >: L <: 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
于 2013-04-05T18:46:31.223 回答
1

我不了解 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)

当我们谈论表达式类型时,我们可以说这两种类型是等价的;当我们谈论变量类型时,它们是不等价的。

于 2013-04-05T18:27:50.467 回答