繁体   English   中英

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

[英]why is specified equivalence between wildcard and existential types not observed in REPL

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

getClass受到编译器的特殊对待:如果在静态类型为T的引用上调用getClass ,则编译器会将getClass的返回类型视为Class 所以这有效:

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

ObjectgetClass方法javadocs提供了更多详细信息:

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

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

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

存在类型的占位符语法

句法:

 WildcardType ::= '_' TypeBounds 

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

...让T = pc [targs,T,targs']为参数化类型,其中targstargs'为空, T为通配符类型_>:L <:U 那么T等于存在类型

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

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

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

我做了什么

在Scala版本中,我尝试使用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一致的存在性类型的两个语句,根据这些语句,失败的语句等同于使用成功的通配符类型的语句?

另外

有问题的错误消息的必需行和找到的行中指定的类型之间有什么区别? 即,这是怎么回事:

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

不同于

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

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

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

令T = pc [targs,T,targs']为参数化类型,其中targs,targs'为空,T为通配符类型_>:L <:U。 那么T等于forSome {type t>:L <:U}的存在类型pc [targs,t,targs],其中t是一些新鲜的类型变量。

特别要注意的是,它说pc[targs, t , targs ] forSome { type t >: L <: U }不是 pc[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

我不了解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<? 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<X> to Class<Y>

这有效

Class<? extends String> var = "foo".getClass();

因为Class<X>(where X<:String)Class<? extends 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)

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM