2010-06-12 83 views
6

我的問題不容易用單詞來解釋,幸運的是這並不難說明。所以,多多包涵:如何正確混合泛型和繼承以獲得期望的結果?

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; 
      } 
     }); 
    } 
} 

基本上,我想一般的「過程」的方法來決定Command對象的泛型參數的類型。目標是能夠將CommandProcessor的不同實現限制爲實現Command接口的某些類,同時能夠調用任何實現了CommandProcessor接口的類的進程方法,並使其返回指定類型的對象參數化的Command對象。我不確定我的解釋是否足夠清楚,如果需要進一步解釋,請告訴我。我想,問題是「這樣做可能嗎?」如果答案是「否」,那麼最好的解決辦法是什麼(我自己想到一對夫婦,但我想要一些新想法)

+0

不應該'BasicCommand'執行'Command'嗎? – 2010-06-12 19:27:46

+0

Touche,固定。謝謝你接受! – Andrey 2010-06-12 19:31:41

回答

3

不幸的是,你不能這樣做。由於您希望根據Command定義​​接口,因此您的實施必須準備採取任何種類的Command實例 - 泛型不能將其限制爲BasicCommand - 如果可以的話,那麼BasicCommandProcessor子類將不會實現接口​​。

或者從另一個角度給出​​接口,泛型不可能確保僅在調用BasicCommand實例時調用此接口。要做到這一點需要知道實現,並且會違背多態和接口的要求。

您可以參數化命令的結果,但不是具體的命令類。

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); 
    } 
} 

最簡單的方法是提供一種方法,接受您需要的特定類型,並在通用方法中調用該方法。 (參見BasicCommandProcessor上方。)

+0

「......給定一個CommandProcessor接口,泛型不可能確保這只是用BasicCommand實例調用的。爲此,需要知道實現,並且會違背多態和接口的要點。」當然如此。如果實現被指定爲類型參數,則子類可能會進一步限制綁定類型,請參閱我的答案。是的,這意味着原始類型不符合替換原則,編譯器需要發出合成方法來轉換方法參數。但它是有效的Java。 – meriton 2010-06-13 10:09:01

+0

我明白你在說什麼。可以通過爲實現細節添加其他參數類型來進行工作,但對於我來說,這有損於具有接口的點。根據我的經驗,隨着接口數量的增長,它也可能變得很難處理。 – mdma 2010-06-13 10:16:30

+0

這取決於意圖。如果CommandProcessor可以在界面中使用具有命令類型的各種命令,確實沒有用處。但是,如果一個CommandProcessor只能使用某些類型的命令(就像這裏所看到的那樣),那麼在接口中表達它可能是合適的。 (請參閱「泛型是否幫助設計並行類層次結構?」,位於http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html) – meriton 2010-06-13 10:32:17

1

基本上,我想通用 「過程」方法來指示命令 對象的通用參數的 類型。

這是在與限定命令類型參數來封閉類型的概念可能性:當實例化​​,實際的類型如Command<String>可以爲C供應。它甚至還可以提供如

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

非泛型類型,什麼會的C<R>的意思是,然後呢? Foo<R>Command<String><R>Command<R>

那麼你有什麼選擇?如果CommandProcessor只需要與特定的返回類型的工作,你可以這樣做:

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

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

} 

不過,我懷疑你想CommandProcessor與整個家庭類型命令的工作。這本身就沒有問題了,只需聲明:

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

但是,如果你還想要命令的不同家庭之間CommandProcessors亞型關係,以便您可以覆蓋process,你大膽超越Java泛型的表現。具體來說,您需要相當於C++的'模板'類型參數(允許將模板作爲實際類型參數傳遞),或者需要捕獲Command的類型參數的能力,給定已知擴展命令的實際類型參數。 Java既不支持。

+0

「也就是說,在實例化CommandProcessor時,必須使用實際類型(如命令)提供給C「 其實,你錯了。一個[通配符](http://java.sun.com/docs/books/tutorial/extra/generics/wildcards.html)(命令)總是可以提供... – Andrey 2010-06-13 00:38:41

+0

我站在更正,並已相應編輯。然而,它在我的論點中變化不大...... – meriton 2010-06-13 09:58:58

+0

你已經很好地描述了這個問題。唯一的事情是CommandProcessor實際上並不需要知道它可能處理的Command的返回類型,除了處理它們時,在這種情況下,它只需要傳遞Command類型的對象被參數化,這似乎不可能做到。 Upvote你備份。 – Andrey 2010-06-13 15:55:58