2010-06-12 7 views
6

Meine Frage ist nicht einfach mit Worten zu erklären, glücklicherweise ist es nicht zu schwierig zu demonstrieren. Also, mit mir tragen:Wie Generika und Vererbung richtig mischen, um das gewünschte Ergebnis zu erhalten?

public interface Command<R> 
{ 
    public R execute();//parameter R is the type of object that will be returned as the result of the execution of this command 
} 

public abstract class BasicCommand<R> implements Command<R> 
{ 
} 

public interface CommandProcessor<C extends Command<?>> 
{ 
    public <R> R process(C<R> command);//this is my question... it's illegal to do, but you understand the idea behind it, right? 
} 

//constrain BasicCommandProcessor to commands that subclass BasicCommand 
public class BasicCommandProcessor<C extends BasicCommand<?>> implements CommandProcessor<C> 
{ 
    //here, only subclasses of BasicCommand should be allowed as arguments but these 
    //BasicCommand object should be parameterized by R, like so: BasicCommand<R> 
    //so the method signature should really be 
    // public <R> R process(BasicCommand<R> command) 
    //which would break the inheritance if the interface's method signature was instead: 
    // public <R> R process(Command<R> command); 
    //I really hope this fully illustrates my conundrum 
    public <R> R process(C<R> command) 
    { 
     return command.execute(); 
    } 
} 

public class CommandContext 
{ 
    public static void main(String... args) 
    { 
     BasicCommandProcessor<BasicCommand<?>> bcp = new BasicCommandProcessor<BasicCommand<?>>(); 
     String textResult = bcp.execute(new BasicCommand<String>() 
     { 
      public String execute() 
      { 
       return "result"; 
      } 
     }); 
     Long numericResult = bcp.execute(new BasicCommand<Long>() 
     { 
      public Long execute() 
      { 
       return 123L; 
      } 
     }); 
    } 
} 

Grundsätzlich mag ich die generische „Prozess“ Methode, um die Art von generischen Parametern des Befehlsobjekts diktieren. Das Ziel besteht darin, verschiedene Implementierungen von CommandProcessor auf bestimmte Klassen zu beschränken, die die Befehlsschnittstelle implementieren, und gleichzeitig die Prozessmethode jeder Klasse aufrufen zu können, die die CommandProcessor-Schnittstelle implementiert und das Objekt des vom Typ angegebenen Typs zurückgibt parametrisiertes Befehlsobjekt. Ich bin mir nicht sicher, ob meine Erklärung klar genug ist. Bitte lassen Sie es mich wissen, wenn weitere Erklärungen benötigt werden. Ich denke, die Frage ist: "Ist das überhaupt möglich?" Wenn die Antwort "Nein" ist, was wäre das beste Work-Around (ich dachte an ein Paar alleine, aber ich hätte gerne ein paar frische Ideen)

+0

Sollte 'BasicCommand'' Command' nicht implementieren? –

+0

Touche, behoben. Danke, dass du das eingefangen hast! – Andrey

Antwort

3

Leider können Sie das nicht tun. Da Sie die CommandProcessor Schnittstelle in Bezug auf Command definiert werden wollen, muss Ihr implemnetation bereit sein, jede Art von Command Beispiel zu nehmen - Generika dies BasicCommand nicht beschränken können - wenn es könnte, dann ist die BasicCommandProcessor Unterklasse würde die CommandProcessor Schnittstelle nicht implementieren.

Oder, aus einem anderen Blickwinkel, mit einer CommandProcessor Schnittstelle, ist es für Generika nicht möglich sicherzustellen, dass dies nur mit BasicCommand Instanzen aufgerufen wurde. Um dies zu tun, müsste man die Implementierung kennen und würde gegen den Punkt des Polymorphismus und der Schnittstellen gehen.

Sie können das Ergebnis des Befehls parametrisieren, aber nicht die konkrete Klasse des Befehls.

public interface Command<R> 
{ 
    public R execute();//parameter R is the type of object that will be returned as the result of the execution of this command 
} 

public abstract class BasicCommand<R> implements Command<R> 
{ 
} 

public interface CommandProcessor 
{ 
    public <R> R process(Command<R> command); 
} 

public class BasicCommandProcessor implements CommandProcessor 
{ 
    public <R> R processBasicCommand(BasicCommand<R> command) 
    { 
     return command.execute(); 
    } 

    public <R> R process(Command<R> command) 
    { 
     return processBasicCommand((BasicCommand<R>)command); 
    } 
} 

Der einfachste Ansatz ist es, ein Verfahren bereitzustellen, das die spezifische Art akzeptieren Sie benötigen, und ruft, dass in der generischen Methode. (Siehe BasicCommandProcessor oben.)

+0

"... bei einer CommandProcessor-Schnittstelle ist es für Generics nicht möglich sicherzustellen, dass dies nur mit BasicCommand-Instanzen aufgerufen wurde. Dazu müsste die Implementierung bekannt sein und würde gegen den Punkt der Polymorphie und der Schnittstellen gehen." Natürlich ist es das. Wenn die Implementierung als Typparameter angegeben wird, kann eine Unterklasse den Typ begrenzen, siehe meine Antwort für ein Beispiel. Ja, das bedeutet, dass die Raw-Typen nicht dem Substitutionsprinzip entsprechen und der Compiler eine synthetische Methode ausgeben muss, um das Methodenargument umzuwandeln. Aber es ist gültig Java. – meriton

+0

Ich sehe was du sagst. Es kann gemacht werden, um zu arbeiten, indem ich zusätzliche Parametertypen für die Implementierungsdetails hinzufüge, aber das beeinträchtigt den Punkt, eine Schnittstelle zu haben. Meiner Erfahrung nach kann es auch unhandlich werden, mit der Anzahl der Schnittstellen zu arbeiten. – mdma

+0

Es hängt von der Absicht ab. Wenn der CommandProcessor mit allen Arten von Befehlen arbeiten kann, ist der Befehlstyp in der Schnittstelle in der Tat nutzlos. Wenn ein CommandProcessor jedoch nur mit bestimmten Befehlstypen arbeiten kann (wie es hier der Fall zu sein scheint), kann dies in der Schnittstelle als angemessen erachtet werden. (Siehe http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html) – meriton

1

Grundsätzlich möchte ich die allgemeine „Prozess“ Methode des Typs generischer Parameter des Befehls Objekt diktieren.

Dies steht im Widerspruch zu der Vorstellung, den Befehl als Typ-Parameter zu dem einschließenden Typ zu definieren: Wenn ein CommandProcessor Instanziierung, eine tatsächliche Art wie Command<String> könnte für C geliefert werden. Es wäre sogar möglich, einen nicht-generischen Typen wie

class Foo implements Command<String> { 
    ... 
} 

zu liefern Was ist die Bedeutung von C<R> wäre dann? Foo<R>? Command<String><R>? Command<R>?

Welche Optionen haben Sie? Wenn ein CommandProcessor nur mit einem bestimmten Rückgabetyp arbeiten, dann können Sie tun:

class CommandProcessor<R, C extends Command<R>> { 
    R process(C command); 
} 

class FancyCommandProcessor<R, C extends FancyCommand<R>> extends CommandProcessor<R,C> { 

} 

Aber ich vermute, dass Sie ein CommandProcessor wollen mit ganzer Typenfamilie von Befehlen zu arbeiten.Das ist an sich kein Problem wäre, erklären einfach:

<R> R process(FancyCommand<R> command); 

Wenn Sie jedoch zusätzlich einen Subtyp Beziehung zwischen CommandProcessors für verschiedene Familien von Befehlen wollen, so dass Sie process außer Kraft setzen können, Sie über die Ausdruckskraft von Java Generics wagen. Insbesondere benötigen Sie entweder die Entsprechung von C++ - Parametern vom Typ 'template' (die das Übergeben einer Vorlage als Argument des tatsächlichen Typs ermöglichen) oder die Fähigkeit, den Typparameter von Command bei einem tatsächlichen type-Argument zu erfassen, von dem bekannt ist, dass es Command erweitert . Java unterstützt keines von beiden.

+0

"Bei der Instanziierung eines CommandProcessors muss ein tatsächlicher Typ wie der Befehl vorhanden sein geliefert für C " Eigentlich liegst du falsch. Eine [Wildcard] (http://java.sun.com/docs/books/tutorial/extra/generics/wildcards.html) (Kommando ) kann immer mitgeliefert werden ... – Andrey

+0

Ich stehe korrigiert und habe entsprechend bearbeitet. Es ändert sich jedoch wenig in meinem Argument ... – meriton

+0

Sie haben das Problem gut beschrieben. Die einzige Sache ist, dass der CommandProcessor eigentlich nichts über den Rückgabetyp des Befehls wissen muss, mit dem er sich befassen könnte, außer wenn es darum geht, sie zu verarbeiten. In diesem Fall muss er nur das Objekt des Typs übergeben, mit dem der Befehl wurde parametrisiert, was scheinbar nicht möglich ist. Upvote Sie zurück. – Andrey

Verwandte Themen