복붙노트

[SCALA] 동적 특성에 혼합

SCALA

동적 특성에 혼합

형질을 갖는

trait Persisted {
  def id: Long
}

어떻게 어떤 경우 클래스의 인스턴스를 받아 혼합 형질과의 복사본을 반환하는 방법을 구현합니까?

메소드의 서명은 다음과 같습니다

def toPersisted[T](instance: T, id: Long): T with Persisted

해결법

  1. ==============================

    1.이은 (공식적으로 2.10.0-M3 이후 스칼라의 일부) 매크로를 수행 할 수 있습니다. 여기 당신을 위해 무엇을 찾고있는 요지의 예입니다.

    이은 (공식적으로 2.10.0-M3 이후 스칼라의 일부) 매크로를 수행 할 수 있습니다. 여기 당신을 위해 무엇을 찾고있는 요지의 예입니다.

    1) 내 매크로 제공된 경우 클래스와 지속 형에서 상속이 지속 된 훨씬 같은 새로운 T 할 것이라고 로컬 클래스를 생성합니다. 그런 다음, 그 인수 (복수 평가를 방지하기 위해)을 캐시하고 생성 된 클래스의 인스턴스를 생성한다.

    2) 어떻게 어떤 나무를 생성하기 위해 알았 는가? 나는 AST를 인쇄하는 간단한 응용 프로그램, parse.exe가 해당 입력 코드를 구문 분석 결과. 난 그냥 구문 분석 클래스 인 $ Persisted1 호출 그래서 (첫째 : 문자열을, 마지막 : String)를 지속 형과 (이름, 성) 사람이 확장 출력을 언급 내 매크로에 재현. parse.exe는 scalac -Xprint에 대한 래퍼입니다 : 파서 ​​-Yshow - 나무 -Ystop-후 : 파서. ,하는 AST를 찾아 "스칼라 2.10에서 메타 프로그래밍"에서 더 많은 읽을 수있는 다른 방법이 있습니다.

    3) 매크로 확장은 scalac에 대한 인수로 -Ymacro 디버그 라이트를 제공하는 경우 확인 전성이 될 수 있습니다. 이 경우 모든 확장가 출력 될 것입니다, 당신은 빠른 CODEGEN 오류를 감지 할 수 있습니다.

    편집하다. 2.10.0-M7 대한 예 업데이트

  2. ==============================

    2.당신이 바닐라 스칼라를 사용하여 원하는 것을 달성 할 수 없습니다. 문제는이나 mixin은 다음과 같은 것입니다 :

    당신이 바닐라 스칼라를 사용하여 원하는 것을 달성 할 수 없습니다. 문제는이나 mixin은 다음과 같은 것입니다 :

    scala> class Foo
    defined class Foo
    
    scala> trait Bar
    defined trait Bar
    
    scala> val fooWithBar = new Foo with Bar
    fooWithBar: Foo with Bar = $anon$1@10ef717
    

    바있는 푸 혼합 만들 수 있지만, 런타임에 수행되지 않습니다. 컴파일러는 단순히 새로운 익명 클래스를 생성합니다 :

    scala> fooWithBar.getClass
    res3: java.lang.Class[_ <: Foo] = class $anon$1
    

    것이 가능 - 스칼라에서 동적 믹스 인을 참조하십시오? 더 많은 정보를 위해서.

  3. ==============================

    3.당신이 할하려는 것은 기록 연결, 스칼라의 타입 시스템이 지원하지 않는 무언가로 알려져있다. (FWIW는, 타입 시스템이 존재한다 - 등이이 같은 -.이 기능을 제공하는)

    당신이 할하려는 것은 기록 연결, 스칼라의 타입 시스템이 지원하지 않는 무언가로 알려져있다. (FWIW는, 타입 시스템이 존재한다 - 등이이 같은 -.이 기능을 제공하는)

    나는 형의 클래스가 사용 사례에 맞게 할 수 있다고 생각하지만, 문제는 당신이 해결하려고하는 어떤 문제에 대한 충분한 정보를 제공하지 않는 한 나는 확실히 말할 수 없다.

  4. ==============================

    4.당신은 SORM 프로젝트의 일환으로 스칼라 2.10.0-RC1의 도구 상자의 API를 이용 날짜 작업 솔루션에 최대를 찾을 수 있습니다.

    당신은 SORM 프로젝트의 일환으로 스칼라 2.10.0-RC1의 도구 상자의 API를 이용 날짜 작업 솔루션에 최대를 찾을 수 있습니다.

    다음 용액 스칼라 2.10.0-M3 반사 API 및 스칼라 인터프리터에 기초한다. 동적으로 생성하고 혼합 형질 원래 케이스 클래스로부터 상속. 감사 캐시 클래스 최대 캐싱이 솔루션은 동적 각 오리지널 케이스 클래스에 대해 하나의 클래스를 작성하고 나중에 재사용한다.

    새로운 반사 API가 많이 공개하는 것이 아니며이 안정적이며에는 튜토리얼이 없기 때문에 아직이 솔루션은 바보 repitative 행동과 단점을 포함 할 수있다.

    다음 코드는 스칼라 2.10.0-M3 테스트되었습니다.

    형질은 혼합한다. 내가 인해 내 프로그램의 업데이트에를 조금 변경 한 것을 바랍니다 참고

    trait Persisted {
      def key: String
    }
    

    실제 작업자 객체

    import tools.nsc.interpreter.IMain
    import tools.nsc._
    import reflect.mirror._
    
    object PersistedEnabler {
    
      def toPersisted[T <: AnyRef](instance: T, key: String)
                                  (implicit instanceTag: TypeTag[T]): T with Persisted = {
        val args = {
          val valuesMap = propertyValuesMap(instance)
          key ::
            methodParams(constructors(instanceTag.tpe).head.typeSignature)
              .map(_.name.decoded.trim)
              .map(valuesMap(_))
        }
    
        persistedClass(instanceTag)
          .getConstructors.head
          .newInstance(args.asInstanceOf[List[Object]]: _*)
          .asInstanceOf[T with Persisted]
      }
    
    
      private val persistedClassCache =
        collection.mutable.Map[TypeTag[_], Class[_]]()
    
      private def persistedClass[T](tag: TypeTag[T]): Class[T with Persisted] = {
        if (persistedClassCache.contains(tag))
          persistedClassCache(tag).asInstanceOf[Class[T with Persisted]]
        else {
          val name = generateName()
    
          val code = {
            val sourceParams =
              methodParams(constructors(tag.tpe).head.typeSignature)
    
            val newParamsList = {
              def paramDeclaration(s: Symbol): String =
                s.name.decoded + ": " + s.typeSignature.toString
              "val key: String" :: sourceParams.map(paramDeclaration) mkString ", "
            }
            val sourceParamsList =
              sourceParams.map(_.name.decoded).mkString(", ")
    
            val copyMethodParamsList =
              sourceParams.map(s => s.name.decoded + ": " + s.typeSignature.toString + " = " + s.name.decoded).mkString(", ")
    
            val copyInstantiationParamsList =
              "key" :: sourceParams.map(_.name.decoded) mkString ", "
    
            """
            class """ + name + """(""" + newParamsList + """)
              extends """ + tag.sym.fullName + """(""" + sourceParamsList + """)
              with """ + typeTag[Persisted].sym.fullName + """ {
                override def copy(""" + copyMethodParamsList + """) =
                  new """ + name + """(""" + copyInstantiationParamsList + """)
              }
            """
          }
    
          interpreter.compileString(code)
          val c =
            interpreter.classLoader.findClass(name)
              .asInstanceOf[Class[T with Persisted]]
    
          interpreter.reset()
    
          persistedClassCache(tag) = c
    
          c
        }
      }
    
      private lazy val interpreter = {
        val settings = new Settings()
        settings.usejavacp.value = true
        new IMain(settings, new NewLinePrintWriter(new ConsoleWriter, true))
      }
    
    
      private var generateNameCounter = 0l
    
      private def generateName() = synchronized {
        generateNameCounter += 1
        "PersistedAnonymous" + generateNameCounter.toString
      }
    
    
      // REFLECTION HELPERS
    
      private def propertyNames(t: Type) =
        t.members.filter(m => !m.isMethod && m.isTerm).map(_.name.decoded.trim)
    
      private def propertyValuesMap[T <: AnyRef](instance: T) = {
        val t = typeOfInstance(instance)
    
        propertyNames(t)
          .map(n => n -> invoke(instance, t.member(newTermName(n)))())
          .toMap
      }
    
      private type MethodType = {def params: List[Symbol]; def resultType: Type}
    
      private def methodParams(t: Type): List[Symbol] =
        t.asInstanceOf[MethodType].params
    
      private def methodResultType(t: Type): Type =
        t.asInstanceOf[MethodType].resultType
    
      private def constructors(t: Type): Iterable[Symbol] =
        t.members.filter(_.kind == "constructor")
    
      private def fullyQualifiedName(s: Symbol): String = {
        def symbolsTree(s: Symbol): List[Symbol] =
          if (s.enclosingTopLevelClass != s)
            s :: symbolsTree(s.enclosingTopLevelClass)
          else if (s.enclosingPackageClass != s)
            s :: symbolsTree(s.enclosingPackageClass)
          else
            Nil
    
        symbolsTree(s)
          .reverseMap(_.name.decoded)
          .drop(1)
          .mkString(".")
      }
    
    }
    

    테스트 응용 프로그램

    import PersistedEnabler._
    
    object Sandbox extends App {
      case class Artist(name: String, genres: Set[Genre])
      case class Genre(name: String)
    
      val artist = Artist("Nirvana", Set(Genre("rock"), Genre("grunge")))
    
      val persisted = toPersisted(artist, "some-key")
    
      assert(persisted.isInstanceOf[Persisted])
      assert(persisted.isInstanceOf[Artist])
      assert(persisted.key == "some-key")
      assert(persisted.name == "Nirvana")
      assert(persisted == artist)  //  an interesting and useful effect
    
      val copy = persisted.copy(name = "Puddle of Mudd")
    
      assert(copy.isInstanceOf[Persisted])
      assert(copy.isInstanceOf[Artist])
      //  the only problem: compiler thinks that `copy` does not implement `Persisted`, so to access `key` we have to specify it manually:
      assert(copy.asInstanceOf[Artist with Persisted].key == "some-key")
      assert(copy.name == "Puddle of Mudd")
      assert(copy != persisted)
    
    }
    
  5. ==============================

    5.그것의 생성 후 객체를 구성 할 수는 없지만 객체 타입 별칭과 정의 구조체를 사용하여 특정 조성물 여부를 결정하는 매우 넓은 테스트를 할 수 있습니다 :

    그것의 생성 후 객체를 구성 할 수는 없지만 객체 타입 별칭과 정의 구조체를 사용하여 특정 조성물 여부를 결정하는 매우 넓은 테스트를 할 수 있습니다 :

      type Persisted = { def id: Long }
    
      class Person {
        def id: Long = 5
        def name = "dude"
      }
    
      def persist(obj: Persisted) = {
        obj.id
      }
    
      persist(new Person)
    

    다음 DEF ID로 모든 개체 : 긴 지속 된 자격을 것입니다.

    나는 당신이하려는 무슨 생각을 달성하는 것은 암시 적 변환 가능합니다 :

      object Persistable {
        type Compatible = { def id: Long }
        implicit def obj2persistable(obj: Compatible) = new Persistable(obj)
      }
      class Persistable(val obj: Persistable.Compatible) {
        def persist() = println("Persisting: " + obj.id)
      }
    
      import Persistable.obj2persistable
      new Person().persist()
    
  6. from https://stackoverflow.com/questions/10373318/mixing-in-a-trait-dynamically by cc-by-sa and MIT license