2013-08-28 2 views
5

В Scala 2.9 можно реализовать полиморфный экземпляра вПолиморфные конкретизации в Scala, используя TypeTag и ClassTag

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

но 2.10 Manifest заменяется с TypeTag, и это мне не ясно, как достичь чего-то аналогично TypeTag. Я бы предпочел, чтобы версия TypeTag сохраняла всю доступную информацию о типе.

Я знаю, что вышеизложенное работает только для черт/классов, для которых не требуются конструкторы args, и тогда он не всегда работает, но он работает достаточно хорошо для того, что мне нужно. Если я смогу улучшить API новых отражений, это будет здорово.

ответ

8

TypeTag еще не обновлено для Manifest, потому что это часть экспериментального и нестабильного отражения Scala. На данный момент вы определенно не должны использовать его для производства.

Для случая использования вы показали, где нужен только класс выполнения (не полный тип информации с дженериков и т.д.), Scala 2.10 введенными ClassTag, которые вы можете использовать, как это:

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

или:

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

В любом случае, Manifest еще не устарели, поэтому, я думаю, вы все равно можете его использовать.

EDIT:

Использование TypeTag для достижения того же:

import scala.reflect.runtime.universe._ 

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

выше решение до сих пор использует некоторые Java отражение. Если мы хотим быть пуристическими и использовать только Scala отражение, это решение:

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

Благодаря @ghik, я все еще был бы интересно узнать, как сделать это с TypeTag, предпочтительно при сохранении полной информации о типе, то есть умозаключение тип должен вывести 'newInstance [MyClass {Int]]: MyClass [Int] ' –

+0

@ DanielMahler Я добавил решение типа TypeTag. См. Мое редактирование. – ghik

+0

@ghik, когда вы используете 'runtimeMirror (this.getClass.getClassLoader)' вы можете получить исключение, если 'T' не загружается с тем же загрузчиком классов, что и' this.type'. Вы должны использовать зеркало, прикрепленное к 'TypeTag':' typeTag [T] .mirror'. –

2

Если вы хотите поддержать прохождение арга, вот трюк я с 2,11: использование

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

Примера:

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)