在Scala中创建现有对象类型的新实例

时间:2015-12-04 04:59:57

标签: scala

我试图根据某种具体类型的实例创建一个新实例。我有一组类型为{B,C}的类型为A类的子类型,我需要一些可调用形式的实用程序,它将采用任何现有的{B,C}实例并允许创建新实例(允许构造) )基于那种类型。

trait A

case class B(name: String) extends A

case class C(name: String) extends A

// I hoped something like this would work, but it doesn't.
def newALike[T <: A](instance: T): T = instance.copy(name = instance.name + "_new") 

所需用法:

val b = B("B")
val c = C("C")

val newB = newALike(b)
newB.name // B_new

val newC = newALike(c)
newC.name // C_new

4 个答案:

答案 0 :(得分:5)

这可行,但可能需要大量重复的代码,具体取决于您的应用程序的具体情况:

trait A {
  def name: String
}

case class B(override val name: String) extends A

case class C(override val name: String) extends A

trait CanCopy[T] {
  def copy(t: T, newName: String): T
}

implicit object canCopyB extends CanCopy[B] {
  override def copy(b: B, newName: String) = b.copy(name=newName)
}

implicit object canCopyC extends CanCopy[C] {
  override def copy(c: C, newName: String) = c.copy(name=newName)
}

def newALike[T <: A](instance: T)(implicit ev: CanCopy[T]): T =
  ev.copy(instance, instance.name + "_new")

问题是特质A无法知道如何构造子类实例的具体细节。正如Scala编译器所看到的那样,没有人知道你可以定义什么作为特征A的扩展,或者它的构造函数可能采用什么参数。 CanCopy和隐式对象告诉Scala编译器“这个是你如何构造一个B,而这个是你构建C的方式。”隐式参数的名称是ev,代表“证据”:它告诉编译器查找可以复制类型T的证据,并且证据由可以完成工作的对象提供。

根据您的应用程序,您可以通过定义另一个特征来避免一些重复的代码,该特征扩展了A,并且B和C扩展,这可以保证具有特定参数的.copy方法可用。然后你可以有一个类型为CanCopy[ThatIntermediaryTrait]的隐式对象,它知道调用.copy方法。

答案 1 :(得分:2)

如果您的定义有效,如果有人定义class D(val x: Int) extends A { def name = "" }并调用newALike(new D(0)),您会发生什么?

使用F-bounded polymorphism替换@BenKovitz的优秀答案:

trait A { def name: String }

// alternately, add type parameter and the method to A
trait CanCopy[T <: CanCopy[T]] { def copy(newName: String): T }

case class B(name: String) extends A with CanCopy[B] {
  def copy(newName: String) = B(newName)
}
// same for C

def newALike[T <: A with CanCopy[T]](instance: T): T = instance.copy(name = instance.name + "_new")

答案 2 :(得分:1)

您可以通过scala宏实现此目的。但这可能有点矫枉过正。

scala> trait A { def name: String }
defined trait A

scala> case class B(name: String) extends A; case class C(name: String) extends A
defined class B
defined class C

scala> def newALikeMacro[T <: A : c.WeakTypeTag](c: Context)(instance: c.Tree): c.Tree = {
     | import c.universe._
     | q"""$instance.copy(name = $instance.name + "_new")""" }
newALikeMacro: [T <: A](c: scala.reflect.macros.blackbox.Context)(instance: c.Tree)(implicit evidence$1: c.WeakTypeTag[T])c.Tree

scala> def newALike[T <: A](instance: T): T = macro newALikeMacro[T]
defined term macro newALike: [T <: A](instance: T)T

scala> val b = B("B"); val c = C("C")
b: B = B(B)
c: C = C(C)

scala> val newB = newALike(b)
newB: B = B(B_new)

scala> val newC = newALike(c)
newC: C = C(C_new)

答案 3 :(得分:0)

您可以尝试使用String myDob= "16/01/1989"; SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); Date date = null; try { date = sdf.parse(myDob); } catch (ParseException e) { // handle exception here ! } java.text.DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(context); String s = dateFormat.format(date); 方法:

copy