2011-03-14 5 views
9

Si tengo una clase C define comola creación de una nueva instancia de un tipo en Scala

class C[A] 

¿hay alguna manera de crear una nueva instancia de A dentro de C? Algo así como

class C[A] { 
    def f(): A = new A() 
} 

entiendo que, si esto fuera posible, lo que probablemente tiene que especificar los argumentos de constructor en alguna parte, y eso está bien.

Si no es posible, ¿hay algún patrón de diseño para tratar el tipo de situación en la que desea crear una nueva instancia de un tipo?

+0

Y qué sugieres que sucede si el concreto tipo no tiene un constructor (sin parámetros)? – Raphael

+0

Idealmente, podría especificar esto en el parámetro tipo. Entonces podría definir C como 'clase C [A (String, String)]' o algo así. Entonces tendría que llamar a A con dos argumentos de Cadena. –

+0

¿Es eso válido Scala? – Raphael

Respuesta

10

Se puede usar un tipo de clase abstracta a instancias:

trait Makeable[T] { 
    def make: T 
} 

class C[T: Makeable] { 
    def f(): T = implicitly[Makeable[T]].make 
} 

Por ejemplo,

implicit object StringIsMakeable extends Makeable[String] { 
    def make: String = "a string" 
} 

val c = new C[String] 
c.f // == "a string" 

Al crear una instancia C, se tendrá que proporcionar, explícita o implícitamente, una makeable que actuará como una fábrica del tipo apropiado. Esa fábrica, por supuesto, sería responsable de proporcionar cualquier argumento de constructor cuando invoca al constructor.

Alternativamente, se puede utilizar un manifiesto, pero se advirtió que este enfoque se basa en la reflexión y no es un tipo seguro:

class C[T: Manifest] { 
    def f(): T = manifest[T].erasure.newInstance.asInstanceOf[T] 
} 

Para completar, también se puede ampliar fácilmente este enfoque para pasar algunas o todas las los parámetros del constructor en el método de la marca:

trait Makeable[Args, T] { def make(a: Args): T } 

class C[Args, T](implicit e: Makeable[Args, T]) { 
    def f(a: Args): T = e.make(a) 
} 

// some examples 
case class Person(firstName: String, lastName: String) 

implicit val personFactory1 = new Makeable[(String, String), Person] { 
    def make(a: (String, String)): Person = Person(a._1, a._2) 
} 
implicit val personFactory2 = new Makeable[String, Person] { 
    def make(a: String): Person = Person(a, "Smith") 
} 

val c1 = new C[String, Person] 
c1.f("Joe") // returns Person("Joe", "Smith") 

val c2 = new C[(String, String), Person] 
c2.f("John", "Smith") // returns Person("John", "Smith") 
5

usted puede exigir un parámetro implícito, así:

class A[T](implicit newT : T) { 
    val t = newT 
} 

Todo lo que necesita entonces es tener una fábrica implícita del tipo deseado en el alcance al instanciar A, p. Ej. las siguientes obras:

implicit def newSeq[T] = Seq[T]()     
val a = new A[Seq[String]]        

como lo demuestra:

scala> a.t 
res22: Seq[String] = List() 
1

El mismo que @ respuesta de Rafael con apply método de una clase de caso:

class Container[A](contained: A) 
case class Person(name: String) 
case class PersonContainer(person: Person) extends Container[Person](person) 
implicit def _ = PersonContainer.apply _ 

class Creator { 
    def deserializeAndPackage[A, B <: Container[A]](data: Array[Byte]) 
          (implicit containerCreator: (A => B)): B = { 
    val p = /* deserialize data as type of A */ 
    containerCreator(p) 
    } 
} 
Cuestiones relacionadas