我想实现一个类似于此的包装类:
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中)?
我怀疑答案是“不”,但我想证实。如果是这样,有没有其他方法可以实现这种通用行为?
答案 0 :(得分:2)
是的,可以在 Scala 中使用,Monoid
可以帮助您实现此功能。
Monoid
输入的lift
Monoid
对象extend
特征中使用Monoid
实现指定类型Monoid
实施custom
方法,如下面的IntMonoid
StringMonoid
。
创建MonoidOp
用于implicit
转换type
MonoidOp
的{{1}},如下所示:WrapperMonoIdOp
和{ {1}}。
代码示例:
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
。它也可以用于字段。这只是一个概念证明,并且有一些限制,可能会或可能不会被修复。