Scala 中的动态混合 - 有可能吗? [英] Dynamic mixin in Scala - is it possible?

查看:18
本文介绍了Scala 中的动态混合 - 有可能吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要实现的是对

def dynamix[A, B](a: A): A with B

我可能知道 B 是什么,但不知道 A 是什么(但如果 B 有 self 类型,那么我可以在 A 上添加一些约束).scala 编译器对上述签名很满意,但我还无法弄清楚实现的样子 - 如果可能的话.

I may know what B is, but don't know what A is (but if B has a self type then I could add some constraints on A). The scala compiler is happy with the above signature, but I could not yet figure out how the implementation would look like - if it is possible at all.

我想到的一些选项:

  • 使用反射/动态代理.
    • 最简单的情况:A 是 Java 级别的接口 + 我可以实例化 B 并且它没有 self 类型.我想这不会太难(除非我遇到一些令人讨厌的、意想不到的问题):
      创建一个新的 B (b),以及一个同时实现 A 和 B 并使用委托给 a 或 b 的调用处理程序的代理.
    • 如果 B 不能被实例化,我仍然可以创建它的一个子类,并按照上面描述的那样做.如果它也有 self 类型,我可能需要在这里和那里进行一些委托,但它可能仍然有效.
    • 但是如果 A 是一个具体类型,而我找不到合适的接口呢?
    • 我是否会遇到更多问题(例如与线性化相关的问题,或有助于 Java 互操作性的特殊结构)?

    您还有其他可行的想法吗?你会推荐哪种方式?期待什么样的挑战"?
    或者我应该忘记它,因为当前的 Scala 限制是不可能的?

    Do you have any other ideas that might work? Which way would you recommend? What kind of "challenges" to expect?
    Or should I forget it, because it is not possible with the current Scala constraints?

    我的问题背后的意图:假设我有一个业务工作流程,但它并不太严格.有些步骤有固定的顺序,有些则没有,但最后所有步骤都必须完成(或其中一些需要进一步处理).
    一个更具体的例子:我有一个 A,我可以添加 B 和 C.我不在乎哪个先完成,但最后我需要一个 A,B 和 C.

    Intention behind my problem: Say I have a business workflow, but it's not too strict. Some steps have fixed order, but others do not, but at the end all of them has to be done (or some of them required for further processing).
    A bit more concrete example: I have an A, I can add B and C to it. I don't care which is done first, but at the end I'll need an A with B with C.

    评论:我对 Groovy 不太了解,但突然出现了这个问题 我想这或多或少与我想要的一样,至少是概念上的.

    Comment: I don't know too much about Groovy but SO popped up this question and I guess it's more or less the same as what I'd like, at least conceptional.

    推荐答案

    我相信这在运行时是不可能做到的,因为特性是在编译时混合到新的 Java 类中的.如果你匿名地将一个特征与一个现有的类混合,你可以看到,查看类文件并使用 javap,scalac 创建了一个匿名的、名称错误的类:

    I believe this is impossible to do strictly at runtime, because traits are mixed in at compile-time into new Java classes. If you mix a trait with an existing class anonymously you can see, looking at the classfiles and using javap, that an anonymous, name-mangled class is created by scalac:

    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.classMain$$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 ,但我不完全确定.

    As you can see, scalac creates a new anonymous class that is loaded at runtime; presumably the method eggs in this anonymous class creates an instance of Spam$class and calls eggs on it, but I'm not completely sure.

    但是,我们可以在这里做一个非常巧妙的技巧:

    However, we can do a pretty hacky trick here:

    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,所以这可能接近于您可以做的最干净的解决方案.这很慢,但记忆可能会有很大帮助.

    Since you need to use the Scala compiler, AFAIK, this is probably close to the cleanest solution you could do to get this. It's quite slow, but memoization would probably help greatly.

    这种方法非常荒谬、老套,而且违背了语言的特点.我想各种奇怪的虫子都可能会爬进来;使用 Java 时间比我长的人警告说,搞乱类加载器会带来疯狂.

    This approach is pretty ridiculous, hacky, and goes against the grain of the language. I imagine all sorts of weirdo bugs could creep in; people who have used Java longer than me warn of the insanity that comes with messing around with classloaders.

    这篇关于Scala 中的动态混合 - 有可能吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆