Ich möchte eine Laufzeitumwandlung in einen generischen Code (asInstanceOf[A]
) ohne implizite Konvertierungen loswerden.Rückgabekopie von Fallklasse aus generischer Funktion ohne Laufzeitumwandlung
Dies passiert, wenn ich ein ziemlich sauberes Datenmodell habe, das aus Fallklassen mit einem gemeinsamen Merkmal besteht und einen generischen Algorithmus implementieren möchte. Als Beispiel sollte der resultierende Algorithmus eine Klasse vom Typ A
nehmen, die eine Unterklasse der trait T
ist und eine Kopie der konkreten Klasse A
mit einem aktualisierten Feld zurückgeben soll.
Dies ist einfach zu erreichen, wenn ich einfach eine abstrakte copy
-Methode zum Basismerkmal hinzufügen und diese in allen Unterklassen implementieren kann. Dies verschmutzt jedoch das Modell möglicherweise mit Methoden, die nur von bestimmten Algorithmen benötigt werden und ist manchmal nicht möglich, weil das Modell außerhalb meiner Kontrolle sein könnte.
Hier ist ein vereinfachtes Beispiel zur Veranschaulichung des Problems und eine Lösung, die Laufzeitumwandlungen verwendet.
Bitte nicht auf die Details hängen.
Angenommen, es ist ein Merkmal und einige Fallklassen ich nicht ändern kann:
trait Share {
def absolute: Int
}
case class CommonShare(
issuedOn: String,
absolute: Int,
percentOfCompany: Float)
extends Share
case class PreferredShare(
issuedOn: String,
absolute: Int,
percentOfCompany: Float)
extends Share
Und hier ist eine einfache Methode, um die aktuellen percentOfCompany
neu zu berechnen, wenn die Gesamtzahl der Aktien geändert haben und aktualisiert das Feld in der Fall Klasse
def recalculateShare[A <: Share](share: A, currentTotalShares: Int): A = {
def copyOfShareWith(newPercentage: Float) = {
share match {
case common: CommonShare => common.copy(percentOfCompany = newPercentage)
case preferred: PreferredShare => preferred.copy(percentOfCompany = newPercentage)
}
}
copyOfShareWith(share.absolute/currentTotalShares.toFloat).asInstanceOf[A]
}
Einige Beispiel Anrufungen auf dem REPL:
scala> recalculateShare(CommonShare("2014-01-01", 100, 0.5f), 400)
res0: CommonShare = CommonShare(2014-01-01,100,0.25)
scala> recalculateShare(PreferredShare("2014-01-01", 50, 0.5f), 400)
res1: PreferredShare = PreferredShare(2014-01-01,50,0.125)
So funktioniert es und so weit ich verstehe, der .asInstanceOf[A]
Aufruf wird nie fehlschlagen, ist aber erforderlich, um den Code kompilieren zu machen. Gibt es eine Möglichkeit, die Laufzeitumwandlung ohne implizite Konvertierungen typsicher zu vermeiden?
Ich glaube nicht. Sie würden einen impliziten Beweis des typetag benötigen. Andernfalls erhalten Sie ein Problem beim Löschen des Typs. –