为什么在REPL中没有观察到通配符和存在类型之间的指定等价

时间:2013-04-05 17:55:43

标签: java scala types existential-type type-bounds

根据 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,是吗?它是什么语言,在哪里指定,以便我能理解错误信息?

2 个答案:

答案 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)

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