2009-10-06 33 views
4

我試圖做交互式Java程序非常簡單的命令行庫。您啓動Java程序並提示您輸入命令。語法是:地圖代表在Java中

> action [object_1, [object_2, [... object_n] ... ]] 

例如:

> addUser name "John Doe" age 42 

這裏action = "addUser", object_1 = "name", object_2 = "John Doe", object_3 = "age", object_4 = "42"

一切之後 動作

對象(即,操作使用對象)。你可以看到動作和對象是簡單的字符串。對象字符串也可以根據需要轉換爲數字。

我的計劃是,此命令行庫的用戶將簡單地創建方法(屬於任何合適的Java對象),並且每個方法分配給一個特定的動作。命令行中的對象成爲用戶分配方法的參數。實現用於該示例的上述方法的合適的類將是:

class Foo { 
    public void addUserHandler(
      String param1, String param2, String param3, Integer param4) { 
     do.someThing(); 
    } 
} 

當用戶鍵入的命令時,由程序員分配的相應的功能獲取與在命令行中指定的參數調用。我知道Java沒有函數指針(如C)或委託(如C#),實現回調的方式是通過一個接口,但是,我不明白我在這種情況下如何使用接口。我看到接口的問題是,他們有:要實現

  1. 的功能的固定數量
  2. 要實現的功能有一個固定的聲明。

與(1)的問題是,我的圖書館的用戶可能決定實施任意數量的功能,儘可能多的行動,因爲他想支持。與(2)的問題是,用戶可能希望與描述性名稱分配的功能,像addUserHandler()爲「ADDUSER」行動。

要是Java委託或函數指針,我只想創建的字符串(代表動作)和代表之間的映射(在程序實際執行的動作)。我想我可以模仿與反思的代表,卻是血淋淋的,我失去的類型安全,我不得不使用字符串名稱的類和方法。有沒有更好的方法?

謝謝,

+0

我失敗看看C#委託或C函數指針(或任何其他靜態類型語言中的其他方法)如何以安全的方式幫助解決固定聲明的問題。你不能有一個包含'Action '和'Action '的C#'Dictionary,同時仍然是類型安全的。 – gustafc

回答

6

如果您希望用戶獲得automagic類型轉換(例如從字符串到整數),則反射是唯一的方法。如果你讓用戶做的工作,那麼你不需要反射(你會得到類型安全):

您的命令接口:

public interface MyCommand { 
    public void execute(String[] args); 
} 

的實現:

public class MyObj { 
    public void doSomething(String param1, Integer param2) { 
    } 

    private void register() { 
    mainApp.registerCommand("doSomething", new MyCommand() { 
     @Override public void execute(String[] args) { 
     doSomething(args[0], Integer.parseInt(args[1])); 
     }}); 
    } 
} 
+0

在這種情況下,doSomething()需要是靜態的嗎? –

+1

不,因爲匿名類的實例有一個指向封閉的MyObj實例的隱式指針。 – gustafc

2

對這個問題的純粹迷人的榮譽。你幾乎可以抵禦Java可以做的事情。 (雖然,當然,委託像你描述的是在任何功能性語言輕而易舉的事。)

首先,限制2不應該是一個問題。從1.5開始,Java不再限制你在方法中的固定聲明。你可以用省略號來表示可變數量的參數,如so

public static double average(double... numbers) { 
    double total = 0.0; 
    for (double d : numbers) { 
     total += d; 
    } 
    return total/numbers.length; 
} 

我不完全知道如何繞過限制#1,但我想的東西用generics和/或anonymous inner classes。我猜在這裏 - 我還沒有嘗試這樣做我自己 - 但是你可以創建一個映射函數名和委託類的,像這樣:在Java中

public interface Callback {...} 

public interface AddUserCallBack extends Callback {...} 

public class UserImpl<T extends Callback> { 
    public T getDelegateRoutine(); 
    ... 
} 

泛型有相關的一些頭髮拉挫折他們,主要是由於類型擦除。您可能需要在接口和抽象基類之前完成你想要的任務。但是像這樣的東西應該適合你。

+0

-1不理解可變參數。這些方法仍然有固定數量的參數 - 省略號只是隱藏數組的語法糖。此外,泛型可能會在這裏得到一個好的解決方案。 – gustafc

1

反射解決方案並不是那麼糟糕。你可以這樣做(語法可能不是100%,Java是不是我最好的語言,我在這裏沒有編譯器):

interface Action { 
    public void Apply(object[] args); 
} 

class AddUser implements Action { 
    Type[] argTypes; 
    public AddUser() { 
     argTypes = {String, String, String, Integer}; 
    } 
    public void Apply(object[] args) { 
     // Now interpret args based on the contents of argTypes, and do your thing. 
     // The map would look like "adduser" => new AddUser() 
     // and could be invoked as map["adduser"].Apply(args) 
    } 
} 

但作爲rtperson說,你在Java的一些基本限制跑起來。幾乎所有其他現代語言都能更好地爲您服務。

+0

恩,我不知道「其他一些神奇的語言」如何讓你在編譯時以類型安全的方式編寫這段代碼?你有一個命令循環處理字符串調用一些方法瓦特/未知參數類型。 _something_必須在運行時進行翻譯,無論是您的代碼還是自動生成的代碼。 – james

+0

非常正確;這個問題必須在運行時解決。在使用易於使用的反射的語言(如Ruby)中,運行時代碼易於編寫和讀取。在使用具有良好靜態宏的語言(如D)時,可以避免生成的轉換對用戶造成不良影響。在Java中,你卡住了。 –

+0

編譯時解決此問題的解決方案是使用自定義註釋。 –