2

我想创建一个生成特征实现的方法。例如:

trait Foo {
  def a
  def b(i:Int):String
}

object Processor {
  def exec(instance: AnyRef, method: String, params: AnyRef*) = {
    //whatever
  }
}

class Bar {
  def wrap[T] = {
    // Here create a new instance of the implementing class, i.e. if T is Foo,
    // generate a new FooImpl(this)
  }
}

我想像这样动态生成FooImpl类:

class FooImpl(val wrapped:AnyRef) extends Foo {
  def a = Processor.exec(wrapped, "a")
  def b(i:Int) = Processor.exec(wrapped, "b", i)
}

手动实现每个特征不是我们想要的(很多样板文件),所以我希望能够在编译时生成 Impl 类。我正在考虑对类进行注释,并且可能编写一个编译器插件,但也许有更简单的方法?任何指针将不胜感激。

4

3 回答 3

3

您可以编写一个宏(宏从 2.10.0-M3 开始正式成为 Scala 的一部分),类似于Mixing in a trait dynamic。不幸的是,现在我没有时间为您编写示例,但请随时在我们的邮件列表中提问,网址为http://groups.google.com/group/scala-internals

于 2012-08-04T21:13:29.423 回答
2

java.lang.reflect.Proxy可以做一些非常接近你想要的事情:

import java.lang.reflect.{InvocationHandler, Method, Proxy}

class Bar {
  def wrap[T : ClassManifest] : T = { 
    val theClass = classManifest[T].erasure.asInstanceOf[Class[T]]
    theClass.cast(
      Proxy.newProxyInstance(
        theClass.getClassLoader(), 
        Array(theClass), 
        new InvocationHandler {
          def invoke(target: AnyRef, method: Method, params: Array[AnyRef])
            = Processor.exec(this, method.getName, params: _*)
        }))
    }
  }

有了它,您无需生成FooImpl.

一个限制是它只适用于没有实现方法的特征。更准确地说,如果在 trait 中实现了一个方法,调用它仍然会路由到处理器,并忽略该实现。

于 2012-08-04T18:10:54.497 回答
1

您可以在ScalaMock中看到三种不同的方法。

ScalaMock 2(当前版本,支持 Scala 2.8.x 和 2.9.x)用于java.lang.reflect.Proxy支持动态类型的模拟和一个编译器插件来生成静态类型的模拟。

ScalaMock 3(目前作为 Scala 2.10.x 的预览版提供)使用宏来支持静态类型的模拟。

假设您可以使用 Scala 2.10.x,我强烈推荐基于宏的方法而不是编译器插件。您当然可以使编译器插件工作(正如 ScalaMock 演示的那样),但这并不容易,而且宏是一种非常优越的方法。

于 2012-08-05T10:04:49.877 回答