【问题标题】:Java or Scala: creating new types at runtimeJava 或 Scala:在运行时创建新类型
【发布时间】:2011-12-09 11:16:44
【问题描述】:

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

【问题讨论】:

标签: java scala reflection


【解决方案1】:

这是不可能的。当然,有一些方法可以在运行时创建新类:只需使用任何 bytecode 操作 library。但是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!

【讨论】:

    【解决方案2】:

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

    class Eaten[T]
    

    Apple.eat 返回一个

    Eaten[Apple]
    

    【讨论】:

    • 显然,我正在尝试实现一个吃水果的模拟。 :) 实际上,我创建了一个 DSL 用于使用 Scala 类型系统描述文档模式。使用匿名声明的类树来描述文档结构。树是不可变的数据结构,但在某些用例中,我需要树上的节点来承载状态。我希望通过混合新类型来表示这种状态的一部分,以便可以保证该状态的依赖关系在编译时存在。
    【解决方案3】:

    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 之类的东西也很简单。

    【讨论】:

      【解决方案4】:

      嗯,在 Scala 中,您可以使用名为 implicit conversions 的东西。但是在 Java 中没有等价物。

      您的代码如下所示:

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

      【讨论】:

        【解决方案5】:

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

        编辑:一些动态功能是coming to scala

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-02-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多