Dynamic mixin in Scala - is it possible?

后端 未结 2 1670
被撕碎了的回忆
被撕碎了的回忆 2020-11-27 04:23

What I\'d like to achieve is having a proper implementation for

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

I may know what B is, but don\'t know wha

2条回答
  •  旧巷少年郎
    2020-11-27 05:12

    I wanted to be able to construct Scala beans in my Spring application context, but I also wanted to be able to specify the mixins to be included in the constructed bean:

    
    
    
      
        
        
    
        
      
    
    

    The difficulty is that Class.forName function does not allow me to specify the mixins. In the end, I extended the above hacky solution to Scala 2.9.1. So, here it is in its full gory; including bits of Spring.

    class ScalaBeanFactory(private val beanType: Class[_ <: AnyRef],
                           private val mixinTypes: Seq[Class[_ <: AnyRef]]) {
      val loader = new DynamicClassLoader
      val clazz = loader.buildClass(beanType, mixinTypes)
    
       def getTypedObject[T] = getObject.asInstanceOf[T]
    
       def getObject = {
         clazz.newInstance()
       }
    
       def getObjectType = null
       def isSingleton = true
    
    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: Class[_ <: AnyRef], vs: Seq[Class[_ <: AnyRef]]) = {
        val id = DynamicClassLoader.uniqueId
    
        val classDef = new StringBuilder
    
        classDef.append("class ").append(id)
        classDef.append(" extends ").append(t.getCanonicalName)
        vs.foreach(c => classDef.append(" with %s".format(c.getCanonicalName)))
    
        val settings = new Settings(null)
        settings.usejavacp.value = true
        val interpreter = new IMain(settings)
    
    
        interpreter.compileString(classDef.toString())
    
    
        val r = interpreter.classLoader.getResourceAsStream(id)
        val o = new ByteArrayOutputStream
        val b = new Array[Byte](16384)
        Stream.continually(r.read(b)).takeWhile(_ > 0).foreach(o.write(b, 0, _))
        val bytes = o.toByteArray
    
        defineClass(id, bytes, 0, bytes.length)
      }
    
    }
    

    The code cannot yet deal with constructors with parameters and does not copy annotations from the parent class’s constructor (should it do that?). However, it gives us a good starting point that is usable in the scala Spring namespace. Of course, don’t just take my word for it, verify it in a Specs2 specification:

    class ScalaBeanFactorySpec extends Specification {
    
      "getTypedObject mixes-in the specified traits" in {
        val f1 = new ScalaBeanFactory(classOf[Cat],
                                      Seq(classOf[Speaking], classOf[Eating]))
    
        val c1 = f1.getTypedObject[Cat with Eating with Speaking]
    
        c1.isInstanceOf[Cat with Eating with Speaking] must_==(true)
    
        c1.speak    // in trait Speaking
        c1.eat      // in trait Eating
        c1.meow     // in class Cat
      }
    
    }
    

提交回复
热议问题