![](/img/trans.png)
[英]Difference between Scala's existential types and Java's wildcard by example?
[英]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
類Object
的getClass
方法的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']為參數化類型,其中targs , targs'為空, 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.