2013-08-28 65 views
5

Trong Scala 2,9 người ta có thể thực hiện instantiation đa hình nhưinstantiation đa hình trong Scala sử dụng TypeTag và ClassTag

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

nhưng đến 2.10 Manifest đang được thay thế bằng TypeTag, và nó không phải là rõ ràng với tôi như thế nào để đạt được một cái gì đó tương tự với TypeTag. Tôi thích nếu phiên bản TypeTag bảo toàn tất cả thông tin loại có sẵn.

Tôi biết rằng ở trên chỉ hoạt động cho các đặc điểm/lớp không yêu cầu người xây dựng, và sau đó nó không phải lúc nào cũng hoạt động, nhưng nó hoạt động đủ tốt cho những gì tôi cần. Nếu tôi có thể làm tốt hơn các API phản chiếu mới sẽ tuyệt vời.

Trả lời

8

TypeTag chưa được thay thế cho Manifest vì đó là một phần của phản xạ Scala thử nghiệm và không ổn định. Bạn chắc chắn không nên sử dụng nó cho sản xuất như của bây giờ.

Đối với trường hợp sử dụng bạn thấy, mà chỉ có lớp thời gian chạy là cần thiết (không đầy đủ loại thông tin với Generics vv), Scala 2.10 giới thiệu ClassTag, mà bạn có thể sử dụng như thế này:

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

hay:

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

Dù sao, Manifest không được chấp nhận, vì vậy tôi đoán bạn vẫn có thể sử dụng.

EDIT:

Sử dụng TypeTag để đạt được cùng:

import scala.reflect.runtime.universe._ 

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

Các giải pháp trên vẫn sử dụng một số phản ánh Java. Nếu chúng ta muốn trở thành puristic và chỉ sử dụng Scala phản ánh, đây là giải pháp:

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

Cảm ơn @ghik, tôi vẫn sẽ quan tâm đến việc tìm hiểu làm thế nào để làm điều đó với TypeTag, tốt nhất trong khi vẫn giữ loại thông tin đầy đủ, tức là kiểu suy luận nên suy ra 'newInstance [MyClass {Int]]: MyClass [Int] ' –

+0

@DanielMahler tôi đã thêm giải pháp 'TypeTag'. Vui lòng xem chỉnh sửa của tôi. – ghik

+0

@ghik khi bạn sử dụng 'runtimeMirror (this.getClass.getClassLoader)' bạn có thể nhận được một ngoại lệ khi 'T' không được nạp với cùng một trình nạp lớp như' this.type'. Bạn nên sử dụng một mirror gắn với 'TypeTag':' typeTag [T] .mirror'. –

2

Nếu bạn muốn hỗ trợ qua args, đây là một thủ thuật tôi làm với 2.11: sử dụng

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

Ví dụ:

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)