2017-02-20 39 views
1

我想要一個static映射,其中的值是實例方法。有點像:在靜態上下文中存儲對實例方法的引用

public class MyClass { 
    static Map<MyEnum, Consumer<String>> methodMapping; 
    static { 
     methodMapping = new EnumMap<>(MyEnum.class); 

     methodMapping.put(MyEnum.FIRST, MyClass::firstMethod); 
     methodMapping.put(MyEnum.SECOND, MyClass::secondMethod); 
    } 
    void firstMethod(String param) { 
     ... 
    } 
    void secondMethod(String param) { 
     ... 
    } 
} 

這給了我一個錯誤,說「非靜態方法不能從靜態上下文中引用」。我明白爲什麼這會是一個問題,如果我嘗試從靜態上下文中調用方法,但不能從實例方法從地圖中檢索方法並將其傳遞給this?像:

MyClass.methodMapping.get(MyEnum.FIRST).accept(this, "string");

+0

這種設計看起來有點古怪。小心解釋這種枚舉到方法映射背後的想法? – Kayaman

+0

@Kayaman重構目的,實例方法中有一個長開關,從每個分支調用不同的方法。我不想爲每個方法中的一種方法創建許多子類。 – Levi

回答

2

這是可以解決的一樣容易改變ConsumerBiConsumer,轉彎MyClass接收器實例的函數的參數:

public class MyClass { 
    static Map<MyEnum, BiConsumer<MyClass,String>> methodMapping; 
    static { 
     methodMapping = new EnumMap<>(MyEnum.class); 

     methodMapping.put(MyEnum.FIRST, MyClass::firstMethod); 
     methodMapping.put(MyEnum.SECOND, MyClass::secondMethod); 
    } 
    void firstMethod(String param) { 
     ... 
    } 
    void secondMethod(String param) { 
     ... 
    } 
    void callTheMethod(MyEnum e, String s) { 
     methodMapping.get(e).accept(this, s); 
    } 
} 
+0

正是我在找什麼,謝謝! – Levi

2

您初始化在靜態初始化塊methodMapping。此時,您的實例方法尚未提及,因爲尚未調用new MyClass()

您可以通過將您的方法設置爲靜態或將methodMapping初始化從靜態塊移動到構造函數來解決此問題。

PS:關鍵字靜態可以從初始化塊可以省略

+0

對不起,但@霍爾格的解決方案正是我所期待的,所以我接受了他的回答,而不是 – Levi

1

看來你不明白,

static Map<MyEnum, Consumer<String>> methodMapping; 
    static { 

正是這麼做的,試圖調用從靜態方法他們不存在的上下文。

這裏要了解的關鍵是:您打算創建一個方法參考;並且方法引用需要某個對象來調用該方法。因此沒有「拖延」;在java中沒有辦法表示「等待this有意義」;或換句話說:在靜態上下文中沒有辦法表達:「稍後您將在非靜態上下文中使用;然後從那裏選擇相應的this」。

+0

你可能是對的。我不知道的是,你已經知道每個實例都有這些方法。爲什麼你不能指定誰是'this',因爲你不想在靜態初始化塊中執行這些方法。 – Levi

+2

但是你需要那個**對象**在那一點上;因爲您想要爲**特定**對象上的方法創建「引用」。 – GhostCat

+0

你走了,我等着看看討論的地方 – Levi

1

是不能夠從一個實例方法來檢索從地圖的方法和把它傳遞this

號公報Consumer僅具有單個參數accept()方法,所以沒有這樣的東西「在通話時間通過this」。

創建方法引用時需要一個實例,所以這個問題歸結爲「不能從靜態上下文中調用實例方法」。

+0

我沒有得到的東西你已經知道每個實例都有這些方法。爲什麼你不能指定這是誰,因爲你不想在靜態初始化塊中執行這些方法。 – Levi

+1

因爲方法引用包含有關實例和方法(或靜態方法的類和方法)的信息。沒有「稍後填寫」選項。 – Kayaman

+0

感謝您的回答 – Levi

1

關鍵是推遲this的規範或更具體:方法被調用的特定實例。因此,不是直接存儲方法引用,而是存儲接受實例並返回該實例的方法引用的函數。

MyClass.java

public class MyClass { 
    static Map<MyEnum, Function<MyClass, Consumer<String>>> methodMapping; 
    static { 
     methodMapping = new EnumMap<>(MyEnum.class); 
     methodMapping.put(MyEnum.FIRST, t -> t::firstMethod); 
     methodMapping.put(MyEnum.SECOND, t -> t::secondMethod); 
    } 
    private String id; 
    public MyClass(String id) { 
     this.id = id; 
    } 
    void firstMethod(String param) { 
     System.out.println(id + ", 1st method, " + param); 
    } 
    void secondMethod(String param) { 
     System.out.println(id + ", 2nd method, " + param); 
    } 
    void dispatchMethod(MyEnum myEnum, String param) { 
     methodMapping.get(myEnum).apply(this).accept(param); 
    } 
} 

主。java的

public class Main { 

    public static void main(String[] args) { 
     MyClass instance = new MyClass("MyInstance"); 
     MyClass.methodMapping.get(MyEnum.FIRST).apply(instance).accept("Using mapping directly"); 
     instance.dispatchMethod(MyEnum.SECOND, "Using dispatch method"); 
    } 

} 

理想methodMapping應該對其他類,所以我建議採取dispatchMethod方法,使methodMapping私人的和不可改變的直接訪問被屏蔽。

相關問題