2015-02-07 10 views
1

딥 런타임 리플렉션을 구현 중입니다. universe.Type이 주어지면 형식에 대한 런타임 클래스 인스턴스를 가져올 수 있습니까? ClassTags는 타입 매개 변수 정보를 잃어 버리는 데 도움이되지 않습니다. 주어진 다음의 클래스 : 아동 typeTag (그리고 아마도 classTag)를 제공하는 경우 스칼라 : 클래스 [_] 인스턴스를 유형 밖으로 가져 오는 방법?

case class One[T](item :T) 
case class Parent(id :Long) 
case class Child(id :Long, parent :One[Parent]) 

, 내가 판단 할 수 wan't, 그 parent.value 런타임 클래스는 클래스 [부모]이어야한다. 물론 더 복잡한 예제를 위해 재귀 적으로 작업하기를 원합니다.

편집 : 내가 조사한 유형의 유형 매개 변수 자체를 식별하는 것이 아니라 그 메소드의 구체적인 반환 유형을 식별하는 데 관심이 있다는 것을 분명히 알 수있었습니다. 좀 더 정확히 말하자면 Typejava.lang.reflect.Method이 주어 졌으므로이 메소드가 반환해야하는 클래스를 결정할 수 있기를 원합니다. 예를 들어, T 형이 별명 또는 둘러싸는 클래스의 형식 매개 변수입니다 반환

  val typeMapping = (parentType.typeConstructor.typeParams zip parentType.typeArgs.map(_.dealias)).toMap 
      System.err.println(s"type mapping for $parentType: ${parentType.typeParams} -> ${parentType.typeArgs}") 
      val methods = 
       for (symbol <- parentType.member(TermName(method.getName)).alternatives; if noArgMethod(symbol)) 
        yield symbol.asMethod 

      val returnType = methods match { 
       case Seq() => throw new IllegalArgumentException(s"coulnd't find a method symbol for $method in type $parentType!. Programming error, sorry") 
       case Seq(m) => 
        val possiblyGeneric = m.asMethod.returnType.dealias 
        typeMapping.getOrElse(possiblyGeneric.typeSymbol, possiblyGeneric) 
       case _ => throw new IllegalArgumentException(s"multiple method symbols for $method in $parentType: $methods") 
      } 

하지만 그것은 더 이상 복잡한 경우에는 실패 할 것으로 예상 : 나는 수동 타입 매핑을 사용하여 내 예를 들어 그것을 할 수 있었다 클래스를 선언하지 않았거나 다른 것을 선언 할 수 있습니다. typeTag를 소유하면 (자), 형태가 완전하게 인스턴스화되고있는 것을 보증하기 때문에 (선언 대신에) abstract 형을 완전하게 인스턴스화 된 형태로 해결할 수 있어야합니다. 나는이 작업이 너무 복잡해서 전용 라이브러리 외부에서 작업을 수행 할 수 없으며 리플렉션 API를 통해 제공해야한다고 생각합니다.

답변

2

당신은 당신이 검사 한 다음 TypeTag 최소한 클래스 이름 얻을 .typeArgs을 사용할 수 있도록하려는 클래스 유형 매개 변수를 넣어 경우

import scala.reflect.runtime.universe._ 

    case class One[T](item :T) 
    case class Parent(id :Long) 
    case class Child[S, T[S]](id: Long, parent: T[S]) 

    def junk[T: TypeTag](v: T) = { 
    val t = implicitly[TypeTag[T]] 
    t.tpe.typeArgs.map { a => 
     val m = runtimeMirror(getClass.getClassLoader) 
     println(m.runtimeClass(a.typeSymbol.asClass)) 
    } 
    } 

    junk(Child(10L, One(Parent(11L)))) 
    junk(List(10,20,30)) 

을 그리고 출력은 다음과 같습니다

class org.example.Ex1$Parent 
class org.example.Ex1$One 

int 

편집 : 미러를 사용하여보다 대표적인 생성자를 얻을 수 있습니다.

def junk[T: TypeTag](v: T) = { 
    val t = implicitly[TypeTag[T]] 
    val m = runtimeMirror(getClass.getClassLoader) 
    val instanceMirror = m.reflectClass(typeOf[T].typeSymbol.asClass) 
    val ctorMethod = typeOf[T].declaration(nme.CONSTRUCTOR).asMethod 
    val ctorRef = instanceMirror.reflectConstructor(ctorMethod) 
    ctorRef(20L, One(Parent(22L))) 
    } 

이러한 도구로는 수행하려는 작업을 수행 할 수 없으면 매크로를 살펴보십시오.

+0

클래스 인스턴스를 가져 오기 위해 편집했습니다. –

+0

그렇습니다. 그러나 스칼라 타입 시스템과 리플렉션 API에 대한 제한된 문서에 대해 모르는 것들이 원래 유형이 완전히 인스턴스화 된 모든 경우에 사용할 수 있는지 궁금합니다. a.typeSymbol.asClass는 제네릭 그 자체가'type' 선언에 의해 정의 된 타입의 기본 런타임 클래스를 반환합니다, 싱글 톤 타입과 그 외의 것들입니까? 필자는이 모든 경우에 반드시 작동 할 필요는 없지만 제한 사항을 알아야하며 도움이되지 않는 오류 메시지를 정상적으로 처리해야합니다. – Turin

+0

여기에는 마법이 없습니다. 유형 매개 변수와 태그를 추가하여 코드가 호출 지점에서 말한 것을 볼 수 있습니다. 그렇지 않으면 클래스 핸들을 사용하면 Java에서의 리플렉션과 같습니다. –

관련 문제