2016-10-10 1 views
1

Ich habe den folgenden Code,scala Karte Methode kann nicht richtig Funktionstyp in der Sammlung mit festen Funktionstypen löst

def obsKPI[T](kpi: Option[T], f: T => Unit) = { 
    kpi match { 
     case Some(obsValue) => f(obsValue) 
     case _ => Unit 
    } 
} 

def func1(str:String):Unit = println(str) 
def func2(num: Int):Unit = println(num) 

//option1: val inputArgs = List((Some("first"),(func1 _)),(Some("third"), (func1 _))) 
//option2: val inputArgs = List((Some(456), (func2 _)),(None,(func2 _))) 
// option3: 
val inputArgs = List((Some("first"),(func1 _)),(Some(456), (func2 _)),(Some("third"), (func1 _)),(None,(func2 _))) 

inputArgs.map(x => obsKPI(x._1, x._2)) 

, wenn entweder die Option 1 oder 2 ausgeführt wird (die inputArgs Listenfunktion von nur String enthält => Einheit oder Int => Unit), funktioniert der Code, aber wenn die Option läuft 3, erhalte ich eine Fehlermeldung:

:68: error: type mismatch; 
found : Int with String => Unit 
required: Any => Unit 
       inputArgs.map(x => obsKPI(x._1, x._2)) 
               ^

Danke, dass ich weiß, was hier schief gelaufen ist.

Antwort

2

Funktionen sind in ihren Parametertypen nicht kovariant (sie sind tatsächlich kontravariant). Das heißt, wenn Foo eine Unterklasse von Bar ist, ist Foo => Unitnicht eine Unterklasse von Bar => Unit (das Gegenteil ist der Fall).

In Ihrem Fall versuchen Sie func1 und func2-Any => Unit zu zwingen, aber das kann nicht funktionieren, weil ihre Typen nicht kompatibel sind - ein String => Unit, und das andere ist Int => Unit ist.

Ein Weg, um dieses Problem zu umgehen, ist eine Fallklasse zu verwenden, anstatt ein Tupel:

case class KPI[T](t: Option[T], f: T => Unit) 
def obsKPI(kpi: KPI[_]) = kpi match { 
    case KPI(Some(k), f) => f(k) 
    case _ =>() // `()` is the value of type Unit. Unit, as you had it is the value of type Unit.type - not what you want at all 
} 
// You can also write the above more concise like this: def obsKPI[T](kpi: KPI[T]) = kpi.t.foreach(kpi.f) 

def func1(str:String) = println(str) 
def func2(num: Int) = println(num) 

val inputArgs = List(KPI(Some("first"),func1 _), KPI(Some(456), func2 _), KPI(Some("third"), func1 _), KPI[Int](None,func2 _)) 

inputArgs.foreach(obsKPI) // Could do .map here too, but ending up with a list of()s is unlikely what you you want. 

Sie können es ein bisschen mehr elegant aussehen, wenn Sie Ihre obsKPI in ein Mitglied der machen Fallklasse:

case class KPI[T](t: Option[T], f: T => Unit) { 
    def obs = t.foreach(f) 
} 
val inputArgs = List(KPI(Some("first"),func1 _), KPI(Some(456), func2 _), KPI(Some("third"), func1 _), KPI[Int](None,func2 _)) 

inputArgs.foreach(_.obs) 
+0

Nach dem Quellcode "endgültige abstrakte Klasse Int private erweitert AnyVal". AnyVal ist wieder Erweiterung von Any. Atleast tis sollte funktionieren, wenn ich out def func1 (str: String) auskommentieren? – BDR

+0

'Int' ist eine Unterklasse von' Any', ja. Aber 'Int => Unit' ist _nicht_ eine Unterklasse von' Any => Unit' Wenn Sie 'func1' entfernen und' func2' nur überall lassen, wird das funktionieren, weil 'inputArgs' eine' List [Int, Int => Unit] ', so wird der' T' in 'obsKPI' Aufruf auf' Int' anstatt auf 'Any' aufgelöst. – Dima

+0

ja es macht sens – BDR

Verwandte Themen