我想實現可以選擇進行類型安全演員(例如,x.cast[List[U]]
)的參數化類(如List[T]
)。使用類型標籤的Typesafe仿製藥(安全演員)
通過類型安全我的意思是如果類型在運行時不正確,則可能會拋出異常,但可以保證如果轉換成功,則結果值的類型爲List[U]
。 (例如,asInstanceOf
沒有做到這一點。List(1,2,3).asInstanceOf[List[String]]
會成功,但返回List
不包含String
秒)
我的方法是代碼,必須支持與TypeTag
鑄造的所有對象。具體來說,我會實施一個Typesafe
特徵與方法cast[U]
,其中暗示TypeTag
類型U
和在運行時檢查是否類型是子類型。這是我設法拿出代碼:
import scala.reflect.runtime.universe.TypeTag
trait Typesafe[+T <: Typesafe[T]] {
val typeTag: TypeTag[_ <: T]
def cast[U](implicit typeTag: TypeTag[U]) = {
if (this.typeTag.tpe <:< typeTag.tpe)
this.asInstanceOf[U]
else
throw new ClassCastException(s"Cannot cast ${this.typeTag} to ${typeTag}")
}
}
的邏輯是:繼承Typesafe[T]
類T
將有實例typeTag
與TypeTag[T]
。然後在cast[U]
中的測試只能成功,如果T
確實是U
的子類型(否則,cast
的隱含參數不存在)。
我們可以按以下步驟實現這個特點(這是設置一個簡單的包裝類):
class TypesafeSet[T](val set : Set[T])
(implicit val typeTag:TypeTag[_<:TypesafeSet[T]])
extends Typesafe[TypesafeSet[T]] {
}
亞型工作,但不幸的是,我們需要每次都指定extends Typesafe[...]
條款。
import scala.collection.immutable.ListSet
class TypesafeListSet[T](set: ListSet[T])
(implicit override val typeTag:TypeTag[_<:TypesafeListSet[T]])
extends TypesafeSet[T](set) with Typesafe[TypesafeListSet[T]] {
}
問題1:我們可以改善這種模式,這樣我們就不必重複extends Typesafe[...]
條款? (目前,如果我們不重複,TypesafeListSet[T]
不能轉換爲TypesafeListSet[T]
。)
然而,在下面的例子中,我們有一個問題:
class TypesafeList[T](val list : List[T])
(implicit val typeTag:TypeTag[_<:TypesafeList[T]])
extends Typesafe[TypesafeList[T]] {
val self = this
def toSet : TypesafeSet[T] = new TypesafeListSet(ListSet(list : _*))
}
toSet
不編譯的方法,因爲編譯器無法解析new TypesafeListSet
的隱式TypeTag[TypesafeListSet[T]]
。需要從typeTag中提取TypeTag[T]
,然後從中重建TypeTag[TypesafeListSet[T]]
。我不知道這是可能的。
問題2:如何獲得所需的TypeTag
在toSet
? (一種選擇將是TypeTag[TypesafeListSet[T]]
類型的隱含參數添加到toSet
,但向外推的問題,泄漏的實現細節,即toSet
使用ListSet
。)
最後,下面的代碼可以寫,違反類型安全:
class TypesafeOption[T](val option : Option[T])
(implicit val typeTag:TypeTag[_<:TypesafeList[T]])
extends Typesafe[TypesafeList[T]] {
}
在這裏,我們有「不小心」在Typesafe
性狀的參數使用TypesafeList
。這編譯好,但這意味着現在TypesafeOption
將有typeTag
爲TypesafeList
!(因此,cast
中的檢查將不正確,並且可能會發生錯誤轉換。)我相信這樣的混音可以很容易地發生,如果編譯器能夠捕捉到這種混音,那就太好了。 (在一定程度上,該類型約束T <: Typesafe[T]
已經避免這種mixups(以下this),但遺憾的是沒有一個在TypesafeOption
。)
問題3(answered):我們可以細化特徵Typesafe
的定義,以便不可能以某種方式實例化Typesafe
,以致cast
行爲不正確?
最後的幾行代碼如何將這些類應使用:
import scala.reflect.runtime.universe.typeTag
object Test {
def main(args:Array[String]) = {
val list = new TypesafeList(List(1,2,3))
val set = list.toSet
val listSet : TypesafeListSet[Int] = set.cast[TypesafeListSet[Int]]
}
}
不幸的是,這段代碼不能編譯。編譯器找不到呼叫new TypesafeList
的TypeTag
。我們需要在該行中明確添加(typeTag[TypesafeList[Int]])
! (原因是,new TypesafeList
需要一個TypeTag[_ <: TypesafeList[Int]]
,編譯器是不夠聰明,看他能只是構建一個TypeTag[TypesafeList[Int]]
。)
問題4:我們如何定義TypesafeList
使一個並不需要明確給TypeTag
s?
最後,我有一個關於整體例如一些問題:
問題5:有(至少)兩個不同的TypeTag
班在運行時,即scala.reflect.runtime.universe.TypeTag
和scala.reflect.api.TypeTags#TypeTag
。哪一個在這裏是正確的?
問題6:我正在比較TypeTags
(即typeTag.tpe
)中包含的類型。我忽略了鏡子。是否應該對鏡子進行比較? (換句話說,如果兩個類型的變量具有兼容的類型,但不同的鏡子,他們將分配兼容?)
問題7:(可能涉及到問題6)會發生什麼,如果類型相同的限定名稱是否已被不同的類加載器加載?上面的代碼在這種情況下是否正確? (即,它不應該是可能的投test.Test
從類加載器加載1,從類加載器加載2 test.Test
,據我瞭解。)
問題8(answered):是TypeTag
這裏的合適的儀器?或者我應該直接用Type
s來標記對象? (畢竟,我僅在cast
中比較了這些類型)。但據我所知(從各種討論中),TypeTag
s是作爲類型安全類標記問題的解決方案提供的。爲什麼?或者TypeTag
是什麼?
問題9:對此表現有何評論?在運行時比較兩種類型(使用<:<
)聽起來可能很昂貴...是否有其他選擇? (我想可能是從TypeTags
-pairs到Boolean
這樣的地圖來記錄哪些類型是分配兼容的,但是這樣的查找速度會更快嗎?TypeTag
s沒有用於快速查找的唯一ID,據我所知。 (GHC爲此使用「指紋」,我想。))
問題10:還有其他意見嗎?我做錯了什麼?我的結論是cast
是類型安全正確的嗎?
LT;博士。你的第一部分看起來像是https://github.com/milessabin/shapeless。 – pedrofurla
更具體地說:https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/typeable.scala –
shapeless/Typeable有一個類似的目標,但是有兩個重要的區別: (a)通過遞歸遍歷數據結構(例如,在list.cast(List [Int])中檢查cast是否安全),它將遍歷列表中的所有元素並檢查它們是否是整數。) (b)需要爲任何可能用作類型參數的類型定義一個Typeable實例。 (即,對於'list.cast(List [Bla])',我們需要一個Typeable [Bla]在範圍內。)這使得它更難作爲一個庫來使用(因爲用戶必須提供Typeable-instances爲他所有的課程。 –