我想实现可以选择进行类型安全演员(例如,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为他所有的课程。 –