从特征自动生成Scala类

时间:2012-08-04 17:04:54

标签: scala

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

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类。我在考虑对类进行注释,也许正在编写一个编译器插件,但也许有一种更简单的方法?任何指针都将受到赞赏。

3 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分: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

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

答案 2 :(得分:1)

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

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

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

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