簡體   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