开发者

Java or Scala: creating new types at runtime

开发者 https://www.devze.com 2023-04-13 09:41 出处:网络
How do I define new types at runtime? I have a factory method that needs to create a new instance of this.type with a marker interface. The mark开发者_如何学Goer interface was not mixed in at compile

How do I define new types at runtime? I have a factory method that needs to create a new instance of this.type with a marker interface. The mark开发者_如何学Goer interface was not mixed in at compile time. I need to find a way to do this at runtime.

I am using Scala, but I think the answer will be general enough to cover both Java and 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!


This is impossible. Sure, there are ways of creating new classes at the runtime: just use any bytecode manipulation library. But this.type is not "the class of this", but the singleton type of this (and there is no way to express "the class of this" in a Scala type signature)! So

def eat: this.type with Eaten = {
    // may do something here, but in the end you have to return
    this
}

And of course if Apple doesn't extend Eaten, it won't compile, whatever you do inside the method. The usual workaround is something like

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]
  }
}

but this won't work if you have more than one marker interface:

val apple = new Apple // Apple
val washed = apple.wash // Apple with Washed
val eaten = washed.eat // Apple with Eaten, but no longer Washed!


I'm not exactly sure what kind of problem you are trying to solve, but maybe instead of implementing a trait you can use something like a type constructor, so Eaten becomes something like

class Eaten[T]

and Apple.eat returns an

Eaten[Apple]


JDK6 will let you compile actual Java code. See http://www.java2s.com/Code/Java/JDK-6/CompilingfromMemory.htm

Alternatively (especially if you wish to create a class to implement an interface), you should check out: java.lang.reflect.Proxy, which will let you do something like this:

 InvocationHandler handler = new MyInvocationHandler(...);
 Class proxyClass = Proxy.getProxyClass(
     Foo.class.getClassLoader(), new Class[] { Foo.class });
 Foo f = (Foo) proxyClass.
     getConstructor(new Class[] { InvocationHandler.class }).
     newInstance(new Object[] { handler });

Note that JMock and the like also make this very straightforward.


Well, in Scala, there's this thing called implicit conversions that you can use. But there's no equivalent in Java though.

Your code would look something like:

implicit def fruit2eaten(fruit: Fruit) = // some way of creating an Eaten from a fruit here.


Now as far as I know (which is not much) scala is not a dynamic language, it is more of a functional language. Now in groovy - its a dynamic language, you can define a class in a string or in a text file and EVAL it at runtime, but I dont believe these things are possible in scala.

Edit : some dynamic features are coming to scala

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号