Scala:对多态类型的单例实例不使用任何内容
给定一个多态性特征,比如Scala:对多态类型的单例实例不使用任何内容,scala,singleton,polymorphism,nothing,Scala,Singleton,Polymorphism,Nothing,给定一个多态性特征,比如 trait Transform[T] { def apply( t: T ) : T } 您可能希望实现各种专用实例,例如 case class Add[Double] extends Transform[Double] { def apply( t: Double ) ... } case class Append[String] extends Transform[String] { def apply( t: String ) ... } 现在一个经常需
trait Transform[T] { def apply( t: T ) : T }
您可能希望实现各种专用实例,例如
case class Add[Double] extends Transform[Double] { def apply( t: Double ) ... }
case class Append[String] extends Transform[String] { def apply( t: String ) ... }
现在一个经常需要的转换也是身份转换。与其为每个类型T专门化标识,不如为所有类型T只使用一个单例实例。我的问题是:在Scala中实现这一点的最佳方法是什么
到目前为止,我发现:看看List[T]是如何实现List.empty[T]和Nil的,我尝试使用Nothing作为类型T。这似乎是有道理的,因为Nothing是所有其他类型的子类型:
object Identity extends Transform[Nothing] {
def apply( t: Nothing ) = t
}
这似乎奏效了。但是,无论我想在哪里按原样使用此实例,如下所示:
val array = Array[Transform[String]]( Transform.Identity )
我得到编译器错误“类型不匹配;找到:Identity.type,required:Transform[String]”。为了使用它,我必须显式地强制转换它:
... Identity.asInstanceOf[Transform[String]]
我不确定这是最好的,甚至是“正确”的方法。谢谢您的建议。由于
Transform[T]
中的类型参数T
是不变的,Transform[Nothing]
不是Transform[String]
的子类型,因此编译器会对此进行投诉。但是在这里使用Nothing
无论如何都没有意义,因为永远不会有Nothing
的实例。那么,如何将一个传递给apply
方法呢?你需要再投一次。我能看到的唯一选择是:
scala> def Id[A] = new Transform[A] { override def apply(t:A) = t }
Id: [A]=> java.lang.Object with Transform[A]
scala> Id(4)
res0: Int = 4
scala> Id("")
res1: java.lang.String = ""
正如@Kim Stebel指出的,
Transform[T]
在T
中是不变的(这是因为T
在def apply(T:T):T)
中同时出现在协变和逆变位置,所以Transform[Nothing]
不是Transform[String]
的子类型,也不能被认为是
如果您主要关心的是每次调用Kim的def Id[A]
时的实例创建,那么您最好的模型就是Predef中的confirms
private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
示例REPL会话
scala> Id("foo")
res0: java.lang.String = foo
scala> Id(23)
res1: Int = 23
好的,使用工厂函数而不是单例实例是可行的,这就是我现在在代码中使用它的方式。但我看到列表使用了一个输入为Nothing(Nil)的单例,所以我认为这样可能更好。传递值不是问题:我会传递字符串或Double或任何我需要的东西。但是,关于类型差异问题,您是对的,这可能是关键。传递值是问题所在。您可以将字符串传递给不需要任何内容的方法,因为Nothing是字符串的子类型,反之亦然。@Kim Stebel我想您的意思是:您不能将字符串传递给不需要任何内容的方法…@Kim Stebel:您是对的。“传递值不是问题”的意思是,当我将值传递到apply()时,我已经持有一个已转换为适当类型的Identity实例,例如Identity[String]。感谢您的回答和评论。因此,投射单例对象是正确的方法。现在,您的代码使用Identity[Any]而不是Identity[Nothing]。这两种方法似乎都管用,就演员而言,这似乎无关紧要。那么,有正确的吗?在lib中,Nil是List[Nothing]类型。
Any
是正确的选择。您希望具有转换类型的值(即,SingletonId.apply
将应用于某些对象),但无法说出它们的类型。另一方面,Nil
中的Nothing
捕获了一个事实,即空列表的头根本没有对应的值。
scala> Id("foo")
res0: java.lang.String = foo
scala> Id(23)
res1: Int = 23