3 Stimmen

Inwiefern ist dies eine Typinkongruenz?

  val eventListeners = new HashMap[Class[Event], ArrayBuffer[Event => Unit]]

  def addEventListener[A <: Event](f: A => Unit)(implicit mf: ClassManifest[A]): A => Unit = {
    eventListeners.getOrElseUpdate(mf.erasure.asInstanceOf[Class[Event]], ArrayBuffer[Event => Unit]()) += f
    f
  }

Wird geworfen:

error: type mismatch;
 found   : (A) => Unit
 required: (this.Event) => Unit
    eventListeners.getOrElseUpdate(mf.erasure.asInstanceOf[Class[Event]], ArrayBuffer[Event => Unit]()) += f

Warum heißt es, dass gefunden wurde (A) => Unit ? Der Wert von f ist eine Funktion, die (Event) => Unit . Ist nicht A nur ein Typparameter, nicht die Signatur?

Beispielanruf: addEventListener { e:FooEvent => .... }

7voto

Daniel C. Sobral Punkte 290004

Die Klasse Function1 ist kontravariant zu seinem Parameter. D.h., sein Typ ist Function1[-T, +R] .

Das bedeutet eine Funktion von Any => Unit ist ein Subtyp von Event => Unit sondern für A ein Subtyp von Event , A => Unit ist ein _super_type von Event => Unit .

Ergo, das Problem. Wenn Sie den Parameter type in A >: Event sollte es funktionieren.

4voto

Rex Kerr Punkte 164629

Sie versprechen Ihre ArrayBuffer dass Sie ihm eine Funktion geben werden, die jede beliebige Event und verwandeln sie in eine Unit (wobei sie vermutlich nebenbei etwas Interessantes tun).

Aber Sie geben ihm eine Funktion, die nur A s, die möglicherweise nicht alle Event s. Das ist eindeutig nicht das, was Sie versprochen haben, also beschwert sich der Compiler.

Sie müssen herausfinden, was in diesem Fall geschehen soll, und entsprechenden Code schreiben. Sie könnten zum Beispiel eine neue Funktion erstellen g die nichts tut, wenn sie eine Event die laut Ihrem Klassenmanifest nicht zu den A und gilt ansonsten f . Oder Sie könnten von allen Zuhörern verlangen, dass sie alle Arten von Ereignissen annehmen und selbst dafür verantwortlich sind, die Ereignisse wegzuwerfen, mit denen sie sich nicht befassen wollen.


Edit: nur um die Dinge mit einem Beispiel zu verdeutlichen,

abstract class Fruit { def tasty: String }

class Banana extends Fruit { def tasty = "Yum!" }

abstract class SeededFruit extends Fruit {
  def seedCount: Int
  def tasty = "Mmm, mmm."
}

class Peach extends SeededFruit { def seedCount = 1 }
class Apple extends SeededFruit { def seedCount = 5 }

val tellAboutSeeds = (sf: SeededFruit) => println("There are "+sf.seedCount+"seeds")

val fruitTeller = new collection.mutable.ArrayBuffer[Fruit=>Unit]
fruitTeller += tellAboutSeeds  // If this worked...
fruitTeller(0)(new Banana)     // ...we'd be in trouble!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X