Java或Scala:在运行时创建新类型

Lan*_*uhn 3 java reflection scala

如何在运行时定义新类型?我有一个工厂方法需要this.type 使用标记接口创建一个新实例.标记接口在编译时未混合.我需要找到一种在运行时执行此操作的方法.

我正在使用Scala,但我认为答案将足以涵盖Java和Scala.

trait Fruit {
    def eat: this.type with Eaten = {
        getClass.getConstructors()(0).newInstance(Array()).asInstanceOf[this.type]; 
        // somehow this needs to return a new instance of this.type with the Eaten trait
        // note that "Apple with Eaten" is not a type that exists at compile-time
    }
}

trait Eaten // marker interface

class Apple extends Fruit

val apple1 = new Apple
val apple2 = a.eat // should return a new Apple with Eaten instance

def eater(eaten: Eaten) = ... // this function only accepts Eaten fruit

eater(apple1) // wont compile!
eater(apple2) // will compile!
Run Code Online (Sandbox Code Playgroud)

Ale*_*nov 5

这是不可能的.当然,有一些方法可以在运行时创建新类:只需使用任何字节码操作.但是,this.type不是 "之类的this",但对单类型this(有没有办法表达"之类的this"在斯卡拉类型签名)!所以

def eat: this.type with Eaten = {
    // may do something here, but in the end you have to return
    this
}
Run Code Online (Sandbox Code Playgroud)

当然,如果Apple不扩展Eaten,它将无法编译,无论你在方法内做什么.通常的解决方法是类似的

class Fruit[F : Manifest <: Fruit[F]] {
  def eat: F with Eaten = {
    val clazz = manifest[F].erasure
    val result = // do your bytecode manipulations here
    result.asInstanceOf[F with Eaten]
  }
}
Run Code Online (Sandbox Code Playgroud)

但是如果你有多个标记界面,这将不起作用:

val apple = new Apple // Apple
val washed = apple.wash // Apple with Washed
val eaten = washed.eat // Apple with Eaten, but no longer Washed!
Run Code Online (Sandbox Code Playgroud)