2013-08-28 10 views
5

w Scala 2.9 można było wdrożyć polimorficzny instancji jakopolimorficzne instancji w Scala użyciu TypeTag i ClassTag

def newInstance[T](implicit m: Manifest[T]) = 
    m.erasure.newInstance.asInstanceOf[T] 

ale od 2.10 Manifest jest zastąpiony TypeTag, i to nie jest dla mnie jasne, w jaki sposób coś osiągnąć podobne z TypeTag. Wolałbym, gdyby wersja TypeTag zachowała wszystkie dostępne informacje o typie.

Wiem, że powyższe działa tylko dla cech/klas, które nie wymagają konstruktora args, i ven wtedy to nie zawsze działa, ale działa wystarczająco dobrze na to, czego potrzebuję. Jeśli mogę zrobić lepiej nowe interfejsy API do refleksji, które byłyby świetne.

Odpowiedz

8

TypeTag nie jest jeszcze zamiennikiem dla Manifest, ponieważ jest częścią eksperymentalnej i niestabilnej refleksji Scala. Zdecydowanie nie powinno się go używać do produkcji od teraz.

Dla przypadku użycia pokazałeś, gdzie potrzebna jest tylko klasa wykonawcze (nie pełne informacje typu z rodzajowych itp), Scala 2.10 wprowadzonych ClassTag, które można używać tak:

def newInstance[T: ClassTag] = 
    implicitly[ClassTag[T]].runtimeClass.newInstance.asInstanceOf[T] 

czyli

def newInstance[T](implicit ct: ClassTag[T]) = 
    ct.runtimeClass.newInstance.asInstanceOf[T] 

W każdym razie, Manifest nie jest przestarzałe, więc myślę, że nadal można z niego korzystać.

EDIT:

Korzystanie TypeTag aby osiągnąć ten sam:

import scala.reflect.runtime.universe._ 

def newInstance[T: TypeTag] = { 
    val clazz = typeTag[T].mirror.runtimeClass(typeOf[T]) 
    clazz.newInstance.asInstanceOf[T] 
} 

Powyższe rozwiązanie nadal wykorzystuje pewne odbicie Java. Jeśli chcemy być purystyczne i używać tylko odbicie Scala, jest to rozwiązanie:

def newInstance[T: TypeTag]: T = { 
    val tpe = typeOf[T] 

    def fail = throw new IllegalArgumentException(s"Cannot instantiate $tpe") 

    val noArgConstructor = tpe.member(nme.CONSTRUCTOR) match { 
    case symbol: TermSymbol => 
     symbol.alternatives.collectFirst { 
     case constr: MethodSymbol if constr.paramss == Nil || constr.paramss == List(Nil) => constr 
     } getOrElse fail 

    case NoSymbol => fail 
    } 
    val classMirror = typeTag[T].mirror.reflectClass(tpe.typeSymbol.asClass) 
    classMirror.reflectConstructor(noArgConstructor).apply().asInstanceOf[T] 
} 
+0

Dzięki @ghik, ja nadal byłby zainteresowany dowiedzieć się, jak to zrobić z TypeTag, najlepiej przy zachowaniu pełnej informacji typu, czyli rodzaj wnioskowania powinna wnioskować 'newInstance [MojaKlasa {Int]]: MojaKlasa [Int] ' –

+0

@DanielMahler Dodałem rozwiązanie oparte na 'TypeTag'. Zobacz moją edycję. – ghik

+0

@ghik przy użyciu 'runtimeMirror (this.getClass.getClassLoader)' można uzyskać wyjątek, gdy 'T' nie jest załadowany z tego samego programu ładującego klasy co' this.type'. Powinieneś użyć lustra dołączonego do 'TypeTag':' typeTag [T] .mirror'. –

2

Jeśli chcesz wesprzeć przekazując argumenty, oto trik zrobić z 2.11:

def newInstance[T : ClassTag](init_args: AnyRef*): T = { 
classTag[T].runtimeClass.getConstructors.head.newInstance(init_args: _*).asInstanceOf[T] 
} 

Przykład użycia:

scala> case class A(x:Double, y:Int) 
defined class A 
scala> newInstance[A](4.5.asInstanceOf[Object],3.asInstanceOf[Object]) 
res1: A = A(4.5,3) 
Powiązane problemy