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

发布于 2024-12-10 22:31:07 字数 797 浏览 0 评论 0原文

如何在运行时定义新类型?我有一个工厂方法,需要使用标记接口创建 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!

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 marker 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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

伊面 2024-12-17 22:31:07

这是不可能的。当然,有多种方法可以在运行时创建新类:只需使用任何字节码操作。但是 this.type 不是“this 的类”,而是 this 的单例类型(并且有无法在 Scala 类型签名中表达“this 的类”)!当然

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

,如果 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]
  }
}

但如果您有多个标记接口,则这将不起作用:

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

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!
棒棒糖 2024-12-17 22:31:07

我不太确定你想要解决什么样的问题,但也许你可以使用类型构造函数之类的东西来代替实现特征,所以 Eaten 变得像

class Eaten[T]

Apple.eat 返回一个

Eaten[Apple]

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]
哽咽笑 2024-12-17 22:31:07

JDK6 将允许您编译实际的 Java 代码。请参阅 http://www.java2s.com/Code/Java/JDK- 6/CompilingfromMemory.htm

或者(特别是如果您希望创建一个类来实现接口),您应该查看:java.lang.reflect.Proxy,这将让你做这样的事情:

 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 });

请注意 JMock 等也可以做到这一点非常简单。

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.

鹊巢 2024-12-17 22:31:07

好吧,在 Scala 中,有一种叫做隐式转换的东西 你可以使用。但 Java 中没有类似的东西。

你的代码看起来像这样:

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

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.
春庭雪 2024-12-17 22:31:07

现在据我所知(不多)scala 不是一种动态语言,它更像是一种函数式语言。现在在groovy中——它是一种动态语言,你可以在字符串或文本文件中定义一个类,并在运行时对其进行EVAL,但我不相信这些事情在scala中是可能的。

编辑:一些动态功能即将登陆scala

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文