2012-08-25 6 views

risposta

52

Gli strumenti di compilazione W.r.t possono eseguire solo espressioni = valori di ritorno, ma non classi risultanti o array di file/byte con risultati di compilazione.

Tuttavia è ancora possibile ottenere ciò che si vuole, dal momento che in Scala è così facile passare da livello di tipo a livello di valore utilizzando valori impliciti:

Modifica. In 2.10.0-RC1 sono stati rinominati alcuni metodi di ToolBox. parseExpr è ora solo parse e runExpr è ora chiamato eval.

scala> import scala.reflect.runtime._ // requires scala-reflect.jar 
             // in REPL it's implicitly added 
             // to the classpath 
             // but in your programs 
             // you need to do this on your own 
import scala.reflect.runtime 

scala> val cm = universe.runtimeMirror(getClass.getClassLoader) 
cm @ 41d0fe80: reflect.runtime.universe.Mirror = JavaMirror with scala.tools.nsc.interpreter.IMain$TranslatingClassLoader... 

scala> import scala.tools.reflect.ToolBox // requires scala-compiler.jar 
              // in REPL it's implicitly added 
              // to the classpath 
              // but in your programs 
              // you need to do this on your own 
import scala.tools.reflect.ToolBox 

scala> val tb = cm.mkToolBox() 
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = [email protected] 

scala> tb.runExpr(tb.parseExpr("class C; scala.reflect.classTag[C].runtimeClass")) 
res2: Any = class __wrapper$1$f9d572ca0d884bca9333e251c64e980d$C$1 

Aggiornamento n. 1. Se non hai bisogno di java.lang.Class e hai solo bisogno di istanziare la classe compilata, puoi scrivere new C direttamente nella stringa inviata a runExpr.

Aggiornamento n. 2. È anche possibile che runExpr utilizzi il mapping personalizzato dai nomi delle variabili ai valori di runtime. Per esempio:

scala> val build = scala.reflect.runtime.universe.build 
build: reflect.runtime.universe.BuildApi = [email protected] 

scala> val x = build.setTypeSignature(build.newFreeTerm("x", 2), typeOf[Int]) 
x: reflect.runtime.universe.FreeTermSymbol = free term x 

scala> tb.runExpr(Apply(Select(Ident(x), newTermName("$plus")), List(Literal(Constant(2))))) 
res0: Any = 4 

In questo esempio creo un termine libero che ha un valore di 2 (il valore non deve essere un primitivo - può essere il vostro oggetto personalizzato) e legare un identificatore ad esso. Questo valore viene quindi utilizzato così com'è nel codice che è compilato ed eseguito da una toolbox.

L'esempio utilizza l'assembly AST manuale, ma è possibile scrivere una funzione che analizza una stringa, individua identificatori non associati, cerca valori in alcuni mapping e quindi crea termini corrispondenti corrispondenti. Non c'è però una tale funzione in Scala 2.10.0.

+0

Grazie! Un follow-up: c'è un modo per me di ottenere un handle su questo 'java.lang.Class' restituito con il riflesso di Scala o dovrò semplicemente attenermi a quello di Java precedente? –

+2

Sicuro. Utilizzare ' .classSymbol ()', dove = 'scala.reflect.runtime.universe.runtimeMirror ( .getClassLoader)'. Quindi si ottiene un simbolo di riflessione Scala, che può essere ispezionato con Scala reflection API. –

+0

Perché hai usato 'universe.runtimeMirror (getClass.getClassLoader)' invece di 'reflect.runtime.currentMirror' e' scala.reflect.classTag [C] .runtimeClass' invece di 'classOf [C]'? Si è rivelato che funzionava bene per la mia parte. Grazie mille per l'aiuto, btw! –