2016-03-26 6 views
1

Ich brauche eine Funktion, die alle Fallklassen ausschließt, die ein Merkmal erweitern. Um ein Beispiel zu gebenAkzeptieren Sie jede Fallklasse, die ein Merkmal als Argument in der Scala erweitert.

Angenommen, es ein Merkmal kann

trait Action { 
    def execute(param: Parameters) 
} 

Und Folgende Parameter werden übergeben wird, je nachdem, welche Art von Maßnahmen ist es

trait Parameters 
case class A(x: String, y: Double) extends Parameters 
case class B(x: Int, y:String, z: Double) extends Parameters 

Benutzer sollten in der Lage zu bestimmen, was sie will als Parameter

class Action1 extends Action { 
    override def execute(param: A) 
} 

class Action2 extends Action { 
    override def execute(param: B) 
} 

und beim Aufruf

def run(t: Action) { 
    t.execute(new A("a", 1.0)) 
} 

Wie erreichen? Wenn ich Action1 implementiere, sagt es, dass ich einen Fehler bekommen werde, den wir nicht überschreiben können, da die Eigenschaft execute akzeptiert Parameter akzeptiert, aber während wir überschreiben, wollen wir eine Klasse, die Parameter erweitert.

Antwort

1

Die Sache, über die Sie nachdenken müssen, ist, warum Sie Ihre Action benötigen, um die execute Methode zu haben, mit zu beginnen. Wahrscheinlich haben Sie so etwas wie dies an anderer Stelle im Code:

def executeAll(actions: Seq[Action], params: Parameters) = 
    actions.foreach { _.execute(params) } 

Beachten Sie, dass Sie hier Ihre actions Liste Aktionen verschiedenen Typen enthalten. Wenn einige davon außer dem Parameter vom Typ A und einige vom Typ B sein müssen, wird es nicht funktionieren, weil Ihr params Parameter nicht beides sein kann.

Aus diesem Grund können Sie eine Methode nicht überschreiben, um einen anderen Parametertyp in einer Unterklasse zu übernehmen (eigentlich können Sie, aber der Typ muss eine Superklasse vom ursprünglichen Typ sein, keine Unterklasse. Sie sagen das Funktionen sind Contravariant in ihren Parametertypen für diese Reas, aber ich schweife ab).

Wenn Ihr Action1 und Action2 tatsächlich allgemein mit ihren Parametern umgehen, durch das Merkmal Schnittstelle, ohne dass sie einer bestimmten Art zu sein, dann ist die Lösung einfach: die Erklärungen Ihrer zwingenden Methoden override def execute(params: Parameters) sein ändern.

Ist dies nicht der Fall ist, und Action1 kann nur mit Parametern vom Typ A, dann (1) gibt es wahrscheinlich (obwohl nicht notwendigerweise) etwas falsch mit Ihrem dewsign beschäftigt, und (2) Sie müssen gehen mit Parametrisierungslösung, wie die andere Antwort suggeriert. Aber dann würden Sie haben alle ihre Plätze tauschen, die sich mit Action auch:

def executeAll[T <: Parameters](actions: Seq[Action[T]], params: T) = ... 

Diese Erklärung Einschränkungen der Typ des params Argument entsprechen, was die Aktionen in der Liste erwarten, so dass Action1.execute nicht sein versehentlich mit B aufgerufen.

Nun, wie Sie im Kommentar zu der anderen Antwort erwähnt haben, können Sie keine Aktionen unterschiedlicher Art mischen, weil es keinen Sinn ergibt: Sie sind im Wesentlichen verschiedene Klassen. Wenn Sie nicht die execute Methode verwenden wollen, und wollen einfach nur die Liste der verschiedenen Aktionen des Codes um einige Stück zu übergeben, können Sie das Wildcard-Syntax verwenden:

val actions: List[Action[_] = List(Action1(...), Action2(...)) 

Wieder einmal diese Liste als dienen kann generischer Container für Aktionen eines beliebigen Typs, aber Sie können ihn nicht verwenden, um die Methode execute generisch anzuwenden, da dies zur Folge hätte, dass der Parameter verschiedene Typen gleichzeitig hat.

4

Eine Möglichkeit besteht darin, Action zu parametrisieren.

trait Action[T <: Parameters] { 
    def execute(param: T) 
} 

Dann

class Action1 extends Action[A] { 
    override def execute(param: A) 
} 

class Action2 extends Action[B] { 
    override def execute(param: B) 
} 

def run[T <: Parameters](t: Action[T], param: T) { 
    t.execute(param) 
} 

// You don't need new for a case class 
run(new Action1, A("a", 1.0)) 

Sie haben run ein wenig hier zu ändern, weil Sie nicht, was zu bekommen, nach dem Sie fragen, allein durch Subklassen. Argumenttypen sind contravariant, was bedeutet, dass eine Funktion A => T eine Superklasse einer Funktion Parameters => T ist. Dies liegt daran, dass während ein beliebiges Argument vom Typ A für A => T und Parameters => T funktioniert, nur einige Argumente vom Typ Parameters für A => T funktionieren, während alle für Parameters => T funktionieren.

+0

Ich musste auch List [Actions] übergeben, wenn ich das Merkmal parametrieren, erlaubt es mir nicht, zu tun "val actionList: Liste [Aktion] = Liste (action1, action2)". Vielleicht fehlt mir etwas in Scala (noch neu dazu :)) – chinmayv

Verwandte Themen