是否可以实现(在Scala中)自动实现包装类的方法的多态包装类?

时间:2017-01-09 07:17:45

标签: scala reflection polymorphism

我想实现一个类似于此的包装类:

class Wrapper[A](wrapped: A) {
  ...
}

我希望它表现得像这样:

val a: Wrapper[Int] = new Wrapper(2)
val b: Wrapper[Int] = new Wrapper(3)

val c = a + b  // "c" should be equal to Wrapper(5)
val d = a * b  // "d" should be equal to Wrapper(6)

val e: Wrapper[String] = new Wrapper("Hello ")
val f: Wrapper[String] = new Wrapper("World")

val g = e + f // "g" should be equal to Wrapper("Hello World")

但我不想在Wrapper类中手动实现“+”和“*”方法;因为一般来说我不知道​​A类会有哪些方法,我希望Wrapper拥有A所有的方法,对于任何A。

这是否可行(在Scala中)?

我怀疑答案是“不”,但我想证实。如果是这样,有没有其他方法可以实现这种通用行为?

4 个答案:

答案 0 :(得分:2)

是的,可以在 Scala 中使用,Monoid可以帮助您实现此功能。

  1. 使用某种方法创建帮助Monoid输入的lift
  2. Monoid对象
  3. 中的extend特征中使用Monoid实现指定类型Monoid
  4. 实施custom方法,如下面的IntMonoid StringMonoid

  5. 创建MonoidOp用于implicit转换type MonoidOp的{​​{1}},如下所示:WrapperMonoIdOp和{ {1}}。

  6. 代码示例:

    implicit def wrapperMonoid

    如果我们使用未实现的类型调用//It's a Monoid that lift a type to Monoid trait Monoid[A] { def mappend(a1: A, a2: A): A } object Monoid { implicit object IntMonoid extends Monoid[Int] { //Implement method with type override def mappend(a1: Int, a2: Int): Int = a1 + a2 } implicit object StringMonoid extends Monoid[String] { override def mappend(a1: String, a2: String): String = a1 + "" + a2 } } trait WrapperMonoIdOp[A] { //MonoidOp it will be used to implicit convert type to the MonoIdOp val F: Monoid[A] val value: A def +(a2: Wrapper[A]): Wrapper[A] = { new Wrapper[A](F.mappend(value, a2.wrapped)) } } class Wrapper[A](val wrapped: A) { override def toString: String = wrapped.toString } object TmpTest { implicit def wrapperMonoid[A: Monoid](a: Wrapper[A]): WrapperMonoIdOp[A] = new WrapperMonoIdOp[A] { //implicit convert the wrapper class to MonoidOp override val F: Monoid[A] = implicitly[Monoid[A]] //implictly Monoiid override val value: A = a.wrapped } def main(args: Array[String]): Unit = { val a = new Wrapper(1) val b = new Wrapper(2) println(a + b) val c = new Wrapper("hello") val d = new Wrapper("world") println(c + d) } } ,编译器将抛出编译错误,如:

    +

    教程:http://eed3si9n.com/learning-scalaz/7.0/sum+function.html

答案 1 :(得分:2)

你不能完全自动完成,你必须实施' +'无论如何,每种类型的功能。一种可能的方法是使用隐式函数:

case class Wrapper[A](wrapped:A) {
  def +(b:Wrapper[A])(implicit sm: (A, A) => A) = Wrapper(sm(wrapped, b.wrapped))
}

object Test {
  def main(args:Array[String]) = {
    implicit val smInt:(Int, Int) => Int = {_+_}
    implicit val smString: (String, String) => String = {_+_}
    val a = Wrapper(2)
    val b = Wrapper(5)
    println(a+b==Wrapper(7))
    val c = Wrapper("Hello ")
    val d = Wrapper("World!")
    println(c+d==Wrapper("Hello World!"))
  }
}

答案 2 :(得分:1)

这是解决评论中澄清的基本问题的答案:我们想要的是拥有一个安全的环境'我们可以在任何类型上操作。应在此安全环境中捕获此类操作的结果。此外,我们希望对在这种情况下捕获的相同类型的实例应用操作,并提供捕获" free"价值观进入上下文。

我认为Monadic背景将满足所有这些要求。无需"实现包裹类型的方法"。 Monads提供了可以进行计算的上下文,并且这样的上下文由monad实现控制。 Monads中的值类似于" Hotel California":可以随时办理登机手续,但他们永远不会离开。

为了说明这一点,让我们称之为Monad" Context":Context[T]

让我们探讨问题中提供的示例:

// Add two values in the context
val a: Context[Int] = Context(2)
val b: Context[Int] = Context(3)

val c = for {
           x <- a
           y <- b
        } yield x+y
// c:Context[Int] = Context(5)

//add a free value to a value in the context
val a: Context[Int] = Context(2)
val c = a.map(value => value + 3)
//c:Context[Int] = Context(5)

// We can transform types within the context and apply arbitrary functions
val transaction = for {
    user <- Context(user)
    amount <- Context(amount)
    account = user.account
    wallet = user.wallet
    funds <- account.withdraw(amount)
} yield wallet.deposit(funds)

需要开发的自定义monad将包含实现业务或技术目标所需的任何计算策略,以及从该自定义上下文安全提取值的方法。有关具体而简单的示例,请参阅this answer

答案 3 :(得分:1)

有可能,有一些限制。

实际上可以获得所需的语法。此解决方案将自动“实现”包装类中存在的任何方法。

import scala.language.dynamics
import scala.reflect.ClassTag
import scala.collection.JavaConverters._

object Temp {

    def main(args: Array[String]): Unit = {
        val test = new Wrapper("foo")
        println(test + "bar")
    }
}

class Wrapper[T](value: T) extends Dynamic {

    private val valueClass = value.getClass

    def applyDynamic(id: String)(parameters: Any*) = {
        val objectParameters = parameters map (x => x.asInstanceOf[Object])
        val parameterClasses = objectParameters map (_.getClass)
        val method = valueClass.getMethod(id, parameterClasses:_*)
        val res = method.invoke(value, objectParameters:_*)
        new Wrapper(res)
    }

    override def toString = value.toString
}

这适用于dynamic个课程。如果在动态类中找不到方法,它将使用方法名称和参数调用applyDynamic。它也可以用于字段。这只是一个概念证明,并且有一些限制,可能会或可能不会被修复。