2012-02-07 16 views
25

Lo que intento hacer es crear una función que tome una clase genérica y use un método estático (lo siento por el lenguaje Java, me refiero al método de su objeto complementario)Obtener el objeto compañero de la clase por tipo genérico dado. Scala

trait Worker {def doSth: Unit} 

class Base 

object Base extends Worker 

// this actually wouldn't work, just to show what I'm trying to achieve 
def callSthStatic[T that companion object is <: Worker](implicit m: Manifest[T]) { 
    // here I want to call T.doSth (on T object) 
    m.getMagicallyCompanionObject.doSth 
} 

¿Alguna idea?

Respuesta

20

A gist by Miles Sabin le puede dar una pista:

trait Companion[T] { 
    type C 
    def apply() : C 
} 

object Companion { 
    implicit def companion[T](implicit comp : Companion[T]) = comp() 
} 

object TestCompanion { 
    trait Foo 

    object Foo { 
    def bar = "wibble" 

    // Per-companion boilerplate for access via implicit resolution 
    implicit def companion = new Companion[Foo] { 
     type C = Foo.type 
     def apply() = Foo 
    } 
    } 

    import Companion._ 

    val fc = companion[Foo] // Type is Foo.type 
    val s = fc.bar   // bar is accessible 
} 

Esto debe ser compilado con la bandera -Ydependent-method-types si se utiliza Scala 2.9.x.

+0

Agregué el contenido real de Gist - el Gist podría desaparecer y el enlace solo califica como un comentario. –

+0

obtengo "error: método dependiente ilegal tipo implicit def companion [T] (implicit comp: Companion [T]) = comp.apply()" at line "implicit imp companion [T] (implicit comp: Companion [ T]) = comp() "con scala 2.9.1. ¿Lo estoy haciendo mal? :-) –

+3

Añadiré entre paréntesis que si el tipo 'Foo' es visible, entonces también lo es su objeto complementario, así que aunque parece bastante ingenioso, no creo que sea útil en la práctica. –

7

Puede usar el reflejo para obtener la clase complementaria y su instancia, pero eso depende de las partes internas de Scala que pueden cambiar en algún futuro lejano (?). Y no hay seguridad de tipo ya que obtiene un AnyRef. Pero no hay necesidad de agregar ningún implícito a sus clases y objetos.

def companionOf[T : Manifest] : Option[AnyRef] = try{ 
    val classOfT = implicitly[Manifest[T]].erasure 
    val companionClassName = classOfT.getName + "$" 
    val companionClass = Class.forName(companionClassName) 
    val moduleField = companionClass.getField("MODULE$") 
    Some(moduleField.get(null)) 
} catch { 
    case e => None 
} 

case class A(i : Int) 

companionOf[A].collect{case a : A.type => a(1)} 
// res1: Option[A] = Some(A(1)) 
+0

Gracias, la reflexión resolvería el problema ... hasta la actualización de la versión scala, ya que no se promete que el nombre del objeto acompañante se mantendrá de esta manera. –

+1

Bueno, eso es cierto. Pero no creo que el nombramiento cambie pronto, ya que esta convención duró al menos desde los primeros días de Scala 2.x. – MxFr

Cuestiones relacionadas