Zwilligsparadoxon dank Type Erasure


Eine häufige Frage ist, was man macht, wenn eine Methodenüberladung „dank“ Type Erasure nicht funktioniert, etwa soetwas:

  def foo(p:List[String]) { println("Strings") }
  def foo(p:List[Int]) { println("Ints") }

Der Compiler sieht hier nur zwei Methoden mit dem gleichen Argument des Typs „List“ und meckert:

Double definition: method foo:(List[String])Unit and method foo:(List[Int])Unit have same type after erasure: (List)Unit.

Für dieses Problem gibt es mehrere Lösungen, z.B, mit impliziten Parametern:

//Idee von Michael Krämer
implicit val s = ""
mplicit val i = 1

def foo(p:List[String])(implicit ignore: String) { println("Strings") }
def foo(p:List[Int])(implicit ignore: Int) { println("Ints") }

Eine andere Variante ist, die Listen-Typen selbst implizit umwandeln zu lassen:

case class IntList(list: List[Int])
case class StringList(list: List[String])

implicit def il(list: List[Int]) = IntList(list)
implicit def sl(list: List[String]) = StringList(list)

def foo(i: IntList) { println("Ints")}
def foo(s: StringList) { println("Strings") }

Der „offensichtliche“ Weg, für jede Version ein Manifest mitzugeben, funktioniert leider nicht, denn Manifest (oder ClassManifest) unterliegt selbst dem Type Erasure – wir haben also nichts gekonnt. Trotzdem gibt es hier einen Weg, Manifests zu verwenden: Wir lassen den Compiler Manifests zählen.

def foo(p: List[String]) { println("Strings") }
def foo[X: ClassManifest](p: List[Int]) { println("Ints") }
def foo[X: ClassManifest, Y: ClassManifest](p: List[Double]) { println("Doubles") }

Natürlich wird das schnell unübersichtlich, man spart sich aber das Hantieren mit impliziten Parametern. Die Typ-Parameter und Manifests spielen im Prinzip keine Rolle, es kommt nur darauf an, dass die Argumentlisten von foo nun unterschiedliche Längen haben, denn intern wird ja jedes ClassManifest zu einen zusätzlichen impliziten Parameter.

Wenn jemand eine hübschere Lösung weiß, möge er sie bitte mitteilen.

Ich habe übrigens die Scala-Link-Seite aktualisiert: Neuer Artikel bei „What’s new in Scala 2.8“, Martin Odersky hat die Firma „Scala Solutions“ für professionellen Scala-Support gegründet, zwei Cheat-Sheets und FeedCluster für Scala hinzugefügt.

Nachtrag

Wenn man genau zwei Methoden hat, gibt es folgende elegante Lösung:

def foo(list: => List[Int]) = { println("Int-List " + list)}
def foo(list: List[String]) = { println("String-List " + list)}
Advertisements

3 Gedanken zu “Zwilligsparadoxon dank Type Erasure

  1. noch eine typsichere Moeglichkeit:

    object xxx {                        
      def foo(p: List[_]) = p match { 
        case (x: String) :: xs => println("strings")
        case (x: Int) :: xs => println("ints")
        case _ => println("oerks") 
      }
        
      def main(args: Array[String]) = {
        foo(List("a", "b"))        
        foo(List(23, 42))
      }
    }   
    

    hth

  2. Nette Idee, ich sehe nur drei Probleme:

    • Leere Listen können nicht zugeordnet werden, für List[Int]() und List[String]() können also keine unterschiedlichen Aktionen ausgeführt werden
    • Der Code weist andere Listen wie List[Date] nicht zur Compilezeit zurück
    • Eine List[Any], die zufällig mit einem String oder Int beginnt, würde das case-statement „austricksen“

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s