2012-08-04 98 views
2

我想創建一個生成特徵實現的方法。例如:從特徵中自動生成一個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類。我正在考慮對類進行註釋,或者編寫一個編譯器插件,但也許有一種更簡單的方法?任何指針將不勝感激。

回答

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

一個限制是它只適用於沒有實現方法的特徵。更確切地說,如果在特徵中實現了一個方法,調用它仍然會路由到處理器,並忽略實現。

+0

謝謝!這看起來不錯,我會試試看。 – 2012-08-05 18:19:52

+0

工作很好!謝謝! – 2012-08-05 20:00:04

3

你可以編寫一個宏(宏從2.10.0-M3開始正式成爲Scala的一部分),沿着Mixing in a trait dynamically的方向。不幸的是,現在我沒有時間爲您編寫一個示例,但可以在我們的郵件列表http://groups.google.com/group/scala-internals上隨時提問。

+0

謝謝!一旦我們可以移動到Scala,我將盡快看看宏。2.10 – 2012-08-05 18:19:33

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所示),但這並不容易,而宏是一種非常優越的方法。

+0

我們將盡快看看Scala 2.10中的宏,同時我將介紹如何應用代理方法。謝謝! – 2012-08-05 18:20:23