Я считаю, что это невозможно сделать строго во время выполнения, потому что во время компиляции черты смешиваются с новыми классами Java. Если вы смешаете черту с существующим классом анонимно, вы увидите, просматривая файлы классов и используя javap, что скалярное имя создает анонимный, искаженный по имени класс:
class Foo {
def bar = 5
}
trait Spam {
def eggs = 10
}
object Main {
def main(args: Array[String]) = {
println((new Foo with Spam).eggs)
}
}
scalac Mixin.scala; ls *.class
возвращает
Foo.class Main$.class Spam$class.class
Main$$anon$1.class Main.class Spam.class
Пока javap Main\$\$anon\$1
возвращает
Compiled from "mixin.scala"
public final class Main$$anon$1 extends Foo implements Spam{
public int eggs();
public Main$$anon$1();
}
Как видите, scalac создает новый анонимный класс, который загружается во время выполнения; предположительно, метод eggs
в этом анонимном классе создает экземпляр Spam$class
и вызывает для него eggs
, но я не совсем уверен.
Однако , мы можем сделать довольно хакерский трюк здесь:
import scala.tools.nsc._;
import scala.reflect.Manifest
object DynamicClassLoader {
private var id = 0
def uniqueId = synchronized { id += 1; "Klass" + id.toString }
}
class DynamicClassLoader extends
java.lang.ClassLoader(getClass.getClassLoader) {
def buildClass[T, V](implicit t: Manifest[T], v: Manifest[V]) = {
// Create a unique ID
val id = DynamicClassLoader.uniqueId
// what's the Scala code we need to generate this class?
val classDef = "class %s extends %s with %s".
format(id, t.toString, v.toString)
println(classDef)
// fire up a new Scala interpreter/compiler
val settings = new Settings(null)
val interpreter = new Interpreter(settings)
// define this class
interpreter.compileAndSaveRun("<anon>", classDef)
// get the bytecode for this new class
val bytes = interpreter.classLoader.getBytesForClass(id)
// define the bytecode using this classloader; cast it to what we expect
defineClass(id, bytes, 0, bytes.length).asInstanceOf[Class[T with V]]
}
}
val loader = new DynamicClassLoader
val instance = loader.buildClass[Foo, Spam].newInstance
instance.bar
// Int = 5
instance.eggs
// Int = 10
Поскольку необходимо для использования компилятора Scala, AFAIK, это, вероятно, близко к самому чистому решению, которое вы могли бы сделать, чтобы получить это. Это довольно медленно, но запоминание, вероятно, очень поможет.
Этот подход довольно смешной, хакерский и идет вразрез с языком. Я предполагаю, что всевозможные странные ошибки могли закрасться; люди, которые использовали Java дольше, чем я, предупреждают о безумии, связанном с вознями с загрузчиками классов.