2017-02-17 4 views
3

nehme ich an einen Satz von Wandlern String haben, als Typklasse:Get Runtime Art von impliziten Beweis gepflückt

import scala.reflect.runtime.universe._ 

abstract class ToStringConverter[T] { 
    def convert(value: T): String 
} 
implicit object IntToStringConverter extends ToStringConverter[Int] { 
    def convert(value: Int) = value.toString 
} 
implicit object DoubleStringConverter extends ToStringConverter[Double] { 
    def convert(value: Double) = value.toString 
} 

und convert-Methode, die die Typinformationen verwendet rechten Konverter zur Auswahl:

def convert[T](v: T)(implicit ev: ToStringConverter[T]): String = ev.convert(v) 

Dies funktioniert gut, wenn ich die konkrete Art im Voraus haben, zum Beispiel:

scala> convert[Double](12.2) 
res0: String = 12.2 

scala> convert[Int](12) 
res1: String = 12 

Ist es möglich, Verwenden Sie die obige Konvertierungsmethode mit einem Laufzeittyp, zum Beispiel mit einem Typ 't' unten?

scala> val t = typeOf[Double] 
t: reflect.runtime.universe.Type = Double 
+0

Ihr 'convert' erwartet einen Wert als Parameter - was möchten Sie mit' t' machen? Wenn Sie 'convert (t)' aufrufen wollen, können Sie 'implizite Objekte bereitstellen. RTUStringConverter erweitert ToStringConverter [runtime.universe.Type]' leicht. – Suma

+0

Die Konvertierungsmethode erwartet derzeit einen Typparameter, und dieser Typ wird verwendet, um den Bereich über den impliziten Mechanismus zum richtigen Konverter zu bringen. Manchmal kenne ich den Typ nicht im Voraus, ich habe nur einen Typ, der es darstellt, also sollte ich die Signatur ändern, um in der Type-Instanz zu übergeben, und manuell irgendwie den impliziten Konverter für diesen Typ erhalten? – gnf

+0

Sie möchten es wie 'val a: Any; called [t] (a)'? – Suma

Antwort

2

Wenn Sie die Auflösungslaufzeit ausführen möchten, ist Reflektion erforderlich, da implizite Kompilierungszeiten aufgelöst werden. Ein Code, wie dies sollte die Arbeit machen:

import scala.reflect.runtime.universe._ 

abstract class ToStringConverterAny { 
    def convertAny(value: Any): String 
} 

abstract class ToStringConverter[T] extends ToStringConverterAny { 
    def convertAny(value: Any): String = convert(value.asInstanceOf[T]) 
    def convert(value: T): String 
} 
implicit object IntToStringConverter extends ToStringConverter[Int] { 
    def convert(value: Int) = value.toString 
} 
implicit object DoubleStringConverter extends ToStringConverter[Double] { 
    def convert(value: Double) = value.toString 
} 

val converters: Map[Type, ToStringConverterAny] = Map(
    typeOf[Int] -> IntToStringConverter, 
    typeOf[Double] -> DoubleStringConverter 
) 

def convert(t: Type, v: Any) = { 
    converters(t).convertAny(v) 
} 

def convert[T](v: T)(implicit ev: ToStringConverter[T]): String = ev.convert(v) 

convert[Double](12.2) 

convert[Int](12) 

val t = typeOf[Double] 
val v: Any = 1.23 

convert(t, v) 

Wenn Sie converters Karte automatisch zu bauen möchten, können Sie auch Reflexion für diese, aber abgeleiteten Klassen Aufzählen erfordert überraschenderweise nicht-triviale Code (einschließlich der Klassenlader - das ist, verständlich, wenn Sie darüber nachdenken).

Wenn Sie die ToStringConverterAny versiegelten können, sollte die Aufzählung über ihre Unterklassen in einem Makro ein wenig einfacher sein.

+0

Das funktioniert gut, aber ich frage mich (wie der Titel der Frage sagt) wenn ich kann den Konverter über impliziten Mechanismus anstatt eine Karte, ich denke, nicht – gnf

+0

implicits sind nur Kompilierzeit. Sie benötigen etwas wie die Karte für den Laufzeitversand. Das Maximum, das Sie tun könnten, wäre, eine Map-Kompilierungszeit unter Verwendung von Makros zu erstellen (Kompilierungszeitreflexion), indem Sie über die Implikationen irgendwie nachdenken. – Suma

Verwandte Themen