2016-12-28 2 views
1

Ich arbeite derzeit an abstraktem Code, der Operationen auf jeder gitterartigen Struktur verarbeiten kann. Anstelle von konkreten Methoden habe ich versucht, ein Framework zu entwickeln, das Lambda-Ausdrücke akzeptiert und Funktionalität basierend auf direktionalen Beziehungen zwischen Grid-Elementen bereitstellt, und ich habe Probleme beim Versuch, einen generischen Typ in diese Methoden zurückzugeben.Generische Rückgabetypen und Lambda-Funktionsargumente

Ich habe eine Arbeitsweise wie die unten, die eine Lambda-Funktion akzeptieren, die das Rasterelement in einer bestimmten Richtung findet und einen Consumer, der eine Methode für jedes der Elemente ausführt.

public static <E> void forEachClockwise(Direction d, Function<Direction, ? extends E> f, Consumer<? super E> c){ 
    /*For each of the orthogonal directions, in order:*/ 
     c.accept(f.apply(d)); 
    /*end For*/ 

} 

Statt mit einem Element Findungsfunktion diese Methode aufgerufen wird, habe ich beschlossen, es wäre gut, jedes Mal passieren eine Schnittstelle ‚Orthogonal‘ zu haben, die eine solche Funktion hat erklärt, und überlasten die forEachClockwise Methode Akzeptiere Elemente dieser Schnittstelle.

Ich habe hier Generika verwendet und erklärt, dass die Consumer-Methode in der Lage wäre, auf Felder der Rasterelemente zuzugreifen, ohne zu werfen. Ich bin besorgt, dass dies eine schlechte Design-Wahl war, da die orthogonale Schnittstelle und die Testklasse, die ich erstellt habe, nicht ohne Warnungen und ohne Guss kompiliert wurden, obwohl ich ein paar verschiedene Dinge versuchte.

public interface Orthogonal { 
    public <E extends Orthogonal> E findNextByDirection(Direction a); 
} 

Die beiden 'gültig' Implementierungen von findNextByDirection Ich habe versucht, sind:

/* in Coordinate implements Orthogonal: */ 
@Override 
public <E extends Orthogonal> E findNextByDirection(Direction a) { 
    return (E) (/*The proper Coordinate*/); 
} 

Und

@Override 
public Coordinate findNextByDirection(Direction a) { 
    return /*The proper Coordinate*/; 
} 
//Warning on return type declaration: 
/*Type safety: The return type Element for findNextByDirection(Direction) from the type Element needs unchecked conversion to conform to E from the type Orthogonal*/ 

optimal, ich möchte ein Verfahren haben, die das forEachClockwise (und andere Methoden) akzeptieren können, die ein Element derselben Klasse zurückgibt, die es aufruft, ohne zu umwandeln. Coordinates findNextByDirection sollte eine Coordinate zurückgeben, Adresse sollte eine Adresse zurückgeben, Cell sollte Cell zurückgeben usw. Andere Fragen und Antworten, die ich mir angeschaut habe, besprechen, wie man mit einer Methode mit einem generischen Rückgabetyp durchkommt, aber nicht gefunden habe Hinweise, wie Sie diese Art von Methode als Lambda-Parameter verwenden können.

Ein paar Sachen, die ich noch nicht ausprobiert habe, machen eine neue findByDirection-Methode, die eine Art Orthogonal und eine Direction als Argumente akzeptiert oder die forEachClockwise-Methode mit einer in Orthogonal definierten Methode aufruft, obwohl diese Ansätze vernünftig klingen als was ich versucht habe. Der andere Ansatz, den ich gesehen habe, ist, der Schnittstelle ihren eigenen generischen Typ zu geben, aber sie fühlt sich eindeutig falsch an und versäumt es, jede orthogonale Klasse als "MyClass implementiert orthogonal" zu deklarieren falsch von Anfang an und erwartet, dass Generika etwas tun, für das sie nicht entwickelt wurden? Oder gibt es eine einfachere Möglichkeit, eine Klasse dazu zu bringen, die richtige Methode aufzurufen und Elemente ihres eigenen Typs generisch zurückzugeben?

Voll Code:

package test; 

import java.util.function.Consumer; 
import java.util.function.Function; 


public enum Direction { 
    NORTH, EAST, SOUTH, WEST; 

    public static <O extends Orthogonal> void forEachClockwise(Direction d, O center, Consumer<O> c){ 
     forEachClockwise(d, center::findNextByDirection, c); 
    } 

    public static <X> void forEachClockwise(Direction d, Function<Direction, ? extends X> f, Consumer<? super X> c){ 
     c.accept(f.apply(d)); 
     forEachExcept(d, f, c); 
    } 

    public static <X> void forEachExcept(Direction d, Function<Direction, ? extends X> f, Consumer<? super X> c){ 
     for(int i=0; i<3; i++){ 
      d = Direction.getClockwise(d); 
      c.accept(f.apply(d)); 
     } 
    } 

    public static Direction getClockwise(Direction d){ 
     switch(d){ 
     case EAST: 
      return SOUTH; 
     case NORTH: 
      return EAST; 
     case SOUTH: 
      return WEST; 
     case WEST: 
      return NORTH; 
     default: 
      return null; 
     } 
    } 

} 

interface Orthogonal { 
    public <E extends Orthogonal> E findNextByDirection(Direction a); 
} 






class Coordinate implements Orthogonal{ 

    int x; 
    int y; 

    public Coordinate(int x, int y){ 
     this.x = x; 
     this.y = y; 
    } 

    public int getX(){ 
     return x; 
    } 

    public int getY() { 
     return y; 
    } 


    @Override //Warning on "Coordinate" below. Compiler suggests writing 
       //entire <E extends Orthogonal> method signature 
    public Coordinate findNextByDirection(Direction a) { 
     switch(a){ 
      case NORTH: 
       return new Coordinate(x+1, y); 
      case SOUTH: 
       return new Coordinate(x-1, y); 
      case EAST: 
       return new Coordinate(x, y+1); 
      case WEST: 
       return new Coordinate(x, y-1); 
      default: 
       return null; 
     } 
    } 
} 
+0

Es wäre hilfreich, wenn Sie ein vollständiges Beispiel erstellen könnten, das kompiliert (mit Warnungen). Diese Implementierung von Orthogonal kompiliert beispielsweise ohne Warnung: 'public class Element implementiert Orthogonal { @Override public Element findNextByDirection (Richtung a) { gib das zurück; } } ' – assylias

+0

die gefundenen Snippet mit einer Warnung für mich nicht kompilieren, auf dem Typdeklaration Element Rückkehr: „Typ Sicherheit: Der Rückgabetyp Element für findNextByDirection (Richtung) vom Typ Element nicht markiert Umstellung auf E entsprechen muss von der Typ Orthagonal " –

+0

haben Sie die Methodensignatur kopiert (Rückgabe eines Elements, kein Orthogonal)? – assylias

Antwort

0

Mein Verständnis ist, dass von Ihrem findNextByDirection Methode, benötigen Sie den gleichen Typ wie die umgebenden Klasse zurückzukehren. In Ihrem Coordinate-Beispiel müssen Sie also eine Koordinate zurückgeben.

Wenn das der Fall ist, können Sie noch einen Schritt weiter mit dem Generika gehen:

interface Orthogonal<E extends Orthogonal<E>> { 
    public E findNextByDirection(Direction a); 
} 
class Coordinate implements Orthogonal<Coordinate> { 
    //rest is the same 
    //you should not have any warnings left 
} 

public static <O extends Orthogonal<O>> void forEachClockwise2(Direction d, O center, Consumer<O> c){ 
    forEachClockwise(d, center::findNextByDirection, c); 
} 

Hinweis: Die beide forEachClockwise Methoden haben einen Konflikt (gleiche rohe Unterschrift) so habe ich einen von ihnen zu forEachClockwise2 umbenannt.

2

Nehmen wir an, es gibt zwei Klassen, die Orthogonal - Coordinate und Area implementieren.

public <E extends Orthogonal> E findNextByDirection(Direction a); 

Was Sie hier deklariert haben, ist eine Templat-Methode. Die Deklaration besagt im Wesentlichen: "In einem bestimmten Kontext gibt diese Methode immer den erwarteten Typ zurück. Wenn das Ergebnis einer Variablen Coordinate zugewiesen werden soll, gibt die Methode eine Coordinate zurück, wenn sie eine Area - Area zurückgibt so weiter - so lange, wie das, was erwartet wird, ein Typ ist, der Orthogonal "

jedoch implementiert, wenn Sie versuchen, es als public Coordinate findNextByDirection(Direction a) in Coordinate zu implementieren, die Sie nicht versprechen mehr zurückkehren, was in einem bestimmten Kontext zu erwarten ist - Sie jetzt nur jemals Coordinate zurückgeben und damit den in der Schnittstelle deklarierten Vertrag brechen. Schlimmer noch, wenn Sie daran denken, gab es keine Möglichkeit, diesen Vertrag überhaupt zu erfüllen.

Sie sollten Ihre Schnittstelle generisch (Orthogonal<E>) deklarieren und die <E extends Orthogonal> aus der Methodendeklaration löschen.

Wenn es die Deklaration class Coordinate implements Orthogonal<Coordinate> ist, die "deutlich falsch" fühlt, sollte es nicht. Dies ist allgemein üblich und wird häufig im SDK selbst verwendet (siehe beispielsweise Comparable).

Wenn Sie es finden, die für findNextByDirection eine Orthogonal zurückzukehren, können Sie den Rückgabetyp als Orthogonal<E> erklären. Sie sollten sogar in der Lage sein, Ihre Methode in Coordinate als public Coordinate findNextByDirection(Direction a) ohne Warnungen aufgrund Java Typ Kovarianz zu implementieren.

+0

"Schlimmer noch, wenn Sie daran denken, gab es keine Möglichkeit, diesen Vertrag überhaupt zu erfüllen." Es sei denn, es gab immer 'null' zurück :) – newacct

Verwandte Themen