2015-11-06 10 views
8

Ich bin auf der Suche nach einer bestimmten Art von Schnittstelle in Java (obwohl das gilt auch für reguläre Klassen). Diese Schnittstelle müsste eine Methode enthalten, beispielsweise invoke; Es würde mit einer variierenden Anzahl von Parametern aufgerufen, abhängig von den generischen Typargumenten, die geliefert wurden.Kann ich in Java eine beliebige Anzahl generischer Typparameter angeben?

Als Beispiel:

public interface Foo<T...> { 
    public void invoke(T... args); 
} 

// In some other class 
public static Foo<Float, String, Integer> bar = new Foo<Float, String, Integer>() { 
    @Override 
    public void invoke(Float arg1, String arg2, Integer arg3) { 
     // Do whatever 
    } 
}; 

Um zu erklären, kurz, wie diese verwendet werden könnten (und etwas Kontext zu machen) betrachten wir eine Klasse Delegator: Die Klasse nimmt eine unterschiedliche Anzahl von generischen Typen und hat eine einzige Methode - invoke, mit diesen Parametertypen. Die Methode gibt ihre Parameter an ein Objekt in einer Liste weiter: eine Instanz von IDelegate, die dieselben generischen Typen verwendet. Dies ermöglicht Delegator, zwischen mehreren Delegate-Methoden zu wählen (definiert in IDelegate), ohne dass für jede spezifische Liste von Parametertypen eine neue Klasse erstellt werden muss.

Ist so etwas verfügbar? Ich habe in C++ über variadic templates gelesen, kann aber in Java nichts Ähnliches finden. Gibt es so etwas? Wenn nicht, was wäre der sauberste Weg, das gleiche Datenmodell zu emulieren?

Antwort

4

Ist so etwas verfügbar? Ich habe gelesen über variadic Vorlagen in C++, kann aber nichts ähnliches in Java finden. Ist eine solche Sache verfügbar?

Nein, diese Funktion ist in Java nicht verfügbar.

2

Nein, es gibt nichts Vergleichbares direkt. Wenn Sie jedoch eine Bibliothek mit Tuple Klassen verwenden, können Sie es simulieren, indem nur die Herstellung der Schnittstelle

interface Foo<T> { 
    void invoke(T t); 
} 

(Diese Schnittstelle im Wesentlichen die gleiche wie Consumer<T> ist.)

Dann könnten Sie zum Beispiel tun

Foo<Tuple<String, Integer, Date, Long>> foo = new Foo<>() { 
    ... 
} 

Sie würden für jede Anzahl von Parametern einen separaten Tuple Typ benötigen. Wenn Sie eine Tuple Klasse für 4 Parameter haben, aber nicht eine für 5, können Sie einen zusätzlichen Parameter mit einer Pair Klasse komprimieren.

Durch Verschachteln von Tupeltypen auf diese Weise erhalten Sie eine unbegrenzte Anzahl von Parametern. Diese Problemumgehungen sind jedoch wirklich hässlich, und ich würde sie nicht verwenden.

+0

Um es so zu sagen: 'Paar ' wäre ausreichend. Aber da niemand ein 'Paar >>>' will, werden solche Dinge normalerweise anders gelöst. Entweder, indem Sie die Sicherheit ganz weglassen, oder mit Bibliotheken wie http://www.javatuples.org/ – Marco13

2

Angesichts des Kontextes, den Sie zur Verfügung gestellt haben, würde ich empfehlen, einen List als Parameter zu verwenden. Wenn diese Parameter etwas gemeinsam haben, können Sie Ihre Liste auf <T extends CommonParrent> statt auf List<Object> beschränken. Wenn nicht, möchten Sie vielleicht weiterhin die Marker-Schnittstelle verwenden.

Hier ist ein Beispiel.

public class Main { 

    public static void main(String[] args) { 
     delegate(asList(new ChildOne(1), new ChildTwo(5), new ChildOne(15))); 
    } 

    private static <T extends Parent> void delegate(List<T> list) { 
     list.forEach(item -> { 
      switch (item.type) { 
       case ONE: delegateOne((ChildOne) item); break; 
       case TWO: delegateTwo((ChildTwo) item); break; 
       default: throw new UnsupportedOperationException("Type not supported: " + item.type); 
      } 
     }); 
    } 

    private static void delegateOne(ChildOne childOne) { 
     System.out.println("child one: x=" + childOne.x); 
    } 

    private static void delegateTwo(ChildTwo childTwo) { 
     System.out.println("child two: abc=" + childTwo.abc); 
    } 

} 

public class Parent { 
    public final Type type; 

    public Parent(Type type) { 
     this.type = type; 
    } 
} 

public enum Type { 
    ONE, TWO 
} 

public class ChildOne extends Parent { 
    public final int x; 

    public ChildOne(int x) { 
     super(Type.ONE); 
     this.x = x; 
    } 
} 

public class ChildTwo extends Parent { 
    public final int abc; 

    public ChildTwo(int abc) { 
     super(Type.TWO); 
     this.abc = abc; 
    } 
} 

Der größte Fehler dieser Lösung ist, dass die Kinder ihre Art über enum angeben müssen, die mit den Abgüssen in der switch-Anweisung entsprechen sollte, so, wenn Sie eine dieser beiden Orte zu ändern, müssen Sie daran denken, ändere das andere, weil der Compiler dir das nicht sagen wird. Sie werden einen solchen Fehler nur finden, indem Sie den Code ausführen und eine bestimmte Verzweigung ausführen, so dass die testgetriebene Entwicklung empfohlen wird.

Verwandte Themen