在静态上下文中存储对实例方法的引用

时间:2017-02-20 13:06:30

标签: java java-8

我想要一个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");

5 个答案:

答案 0 :(得分:3)

这可以解决,就像将Consumer更改为BiConsumer一样简单,将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);
    }
}

答案 1 :(得分:2)

您在静态初始化块中初始化methodMapping。此时,您的实例方法尚未被引用,因为您尚未调用new MyClass()

您可以通过将方法设置为静态,或将methodMapping初始化从静态块移动到构造函数来解决此问题。

PS:初始化块

中可以省略关键字 static

答案 2 :(得分:1)

似乎你不明白

didFinishLaunchingWithOptions

正是如此,试图从他们不存在的静态上下文中调用这些方法。

这里要理解的关键是:您打算创建方法参考;并且方法引用需要一些对象来调用该方法。因此,没有&#34;延迟&#34 ;; java中没有办法表达&#34;等待launchOptions有意义&#34 ;;或换句话说:在静态上下文中无法表达:&#34;稍后您将在非静态上下文中使用;然后从那里选择相应的didFinishLaunchingWithOptions&#34;。

答案 3 :(得分:1)

  

是否可以从实例方法中从地图中检索方法并将其传递给this

没有。 Consumer只有一个参数accept()方法,所以没有&#34;在通话时间传递this&#34;。

创建方法引用时需要一个实例,因此这些问题归结为&#34;无法从静态上下文中调用实例方法&#34;。

答案 4 :(得分: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);
    }
}

<强> Main.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私有且不可变。