3

Ich verwende eine benutzerdefinierte unveränderliche Entitätsklasse, die Schlüssel verwendet, die Case-Objekte sind, die Feld [+ T] erweitert, wobei T kovariant ist. Es macht Sinn, weil ein Field [Stream [Int]] als Field [Seq [Int]] behandelt werden kann.Scala: Zugriff auf Eigenschaften mit kovarianten Feld [+ T]

object ProblemWithCovariant { 

    sealed trait Field[+T] 
    case object _username extends Field[String] 
    case object _email extends Field[String] 
    case object _age extends Field[Byte] 

    case class Entity(attrs: Map[Field[Any], Any]) { 
    def apply[T](field: Field[T]): T = attrs(field).asInstanceOf[T] 
    def set[T](field: Field[T])(value: T): Entity = copy(attrs = attrs.updated(field, value)) 
    } 

    def main(args: Array[String]): Unit = { 
    val user = Entity(Map.empty) 
     .set(_username)("John") 
     .set(_age)(23) 

    val username: String = user(_username) // alright 
    val ageInt: Int = user(_age) // compile error 
    } 

} 

Dies jedoch führt zu einem Compiler-Fehler, wenn ich versuche, einen Byte-Wert abrufen und auf ein Int-Feld zuordnen. Mir ist bewusst, dass Byte kein Untertyp von Int ist und ihre einzige Beziehung eine implizite Umwandlung ist. Zu meiner Überraschung wurde diese Konvertierung im obigen Beispiel nicht aufgerufen und der Compiler fordert mich nun auf, ein Feld [Int] zu übergeben!

Nachdem ich einige Zeit experimentiert hatte, erkannte ich, dass ich den Typ explizit bereitstellen konnte, wie zum Beispiel val ageInt: Int = user[Byte](_age). Obwohl dies funktioniert, macht es meinen Code ausführlicher, als ich es vorziehen würde. Gibt es eine Möglichkeit, die Signatur von Entity.apply so zu ändern, dass sie den Typ des Parameters field berücksichtigt?

Antwort

4
def apply[T, U](field: Field[U])(implicit ev: U => T): T = 
    ev(attrs(field).asInstanceOf[U]) 

funktioniert.

+0

Ich verstehe ein paar Dinge nicht. 1. Sie verwenden die Funktion 'ev' in der Methode, ohne sie zu definieren. Wie funktioniert das und was ist der Typ T in Ihrer Lösung? 2. Irgendeine Idee, warum die numerische Erweiterung von Byte zu Int im ursprünglichen Code des OP nicht auftritt? – Samar

+0

1. 'ev' ist ein Parameter. Genau wie wenn Sie 'def f (x: Int) = ...' schreiben, verwenden Sie 'x', ohne es zu definieren. Die Typen 'T' und' U' unterscheiden sich für verschiedene Aufrufe von 'apply': Sie sind auch Parameter. Aber für 'val AgeInt: Int = Benutzer (_age)', 'T' ist' Int' und 'U' ist' Byte'. –

+0

Eigentlich war ich verwirrt, welche implizite Definition in diesem Bereich verwendet wurde. Wusste nicht, dass der Compiler "Predef. $ Conforms" als "ev" in diesem Fall übernimmt. – Samar

Verwandte Themen