Search code examples
javaenumsconstructornon-staticmethod-reference

Non static method reference in enum constructor as parameter


I am trying to map some values to functions that should be applied to these values. if simplify, i want do so:

import java.util.function.Supplier;

public enum SomeEnum {
    CASE1 ("val1", this::doAction1),
    CASE2 ("val2", this::doAction1),
    CASE3 ("val3", this::doAction2),
    CASE4 ("val4", this::doAction2);

    String value;
    Supplier<String> action;

    SomeEnum(String value, Supplier<String> action) {
        this.value = value;
        this.action = action;
    }

    private String doAction1() {
        return this.value + "-> action1";
    }

    private String doAction2() {
        return this.value + "-> action2";
    }
}

It does not compile because of SomeEnum.this cannot be referenced from a static context. I can't make doAction1 & doAction2 static, because I need the value from an instance.

Here is if-else/switch based solution:

import java.util.function.Supplier;

public enum SomeEnum {
    CASE1 ("val1", SomeEnum.ACTION1),
    CASE2 ("val2", SomeEnum.ACTION1),
    CASE3 ("val3", SomeEnum.ACTION2),
    CASE4 ("val4", SomeEnum.ACTION2);

    final static boolean ACTION1 = true;
    final static boolean ACTION2 = false;

    String value;
    Supplier<String> action;

    SomeEnum(String value, boolean flag) {
        this.value = value;
        this.action = flag ? this::doAction1 : this::doAction2;
    }

    private String doAction1() {
        return this.value + "-> action1";
    }

    private String doAction2() {
        return this.value + "-> action2";
    }
}

Also, I have more than 2 methods and I need extra enum instead of Boolean flag and switch block in Constructor to map to Functions. This enum was created to avoid an insane switch block in my legacy world. If it will be in constructor again, I won't win anything.

And my main question: If it is possible to use a non-static method reference inside the constructor, why can't I pass it as a parameter? Could you please explain why this isn't allowed

enum SomeEnum {
    CASE1 (this::doAction);
    Supplier<String> action;
    SomeEnum(Supplier<String> action) {
        this.action = action;
    }
private String doAction() {
            return "action";
        }
}

when this is ok

enum SomeEnum {
    CASE1;
    Supplier<String> action;
    SomeEnum() {
        this.action = this::doAction;
    }
    private String doAction() {
        return "action";
    }
}

The only solution, that I found:

import java.util.function.Supplier;

public enum SomeEnum {
    CASE1 ("val1", () -> SomeEnum.doAction1("val1")),
    CASE2 ("val2", () -> SomeEnum.doAction1("val2")),
    CASE3 ("val3", () -> SomeEnum.doAction2("val3")),
    CASE4 ("val4", () -> SomeEnum.doAction2("val4"));

    String value;
    Supplier<String> action;

    SomeEnum(String value, Supplier<String> action) {
        this.value = value;
        this.action = action;
    }

    private static String doAction1(String value) {
        return value + "-> action1";
    }

    private static String doAction2(String value) {
        return value + "-> action2";
    }
}

Could you please evaluate if it's a good one. And why if it isn't.


Solution

  • You are trying to implement something that seems fundamentally wrong to me. Did you know you can have abstract methods in enums and provide their implementation for each constant?

    The interface of the enum is clear: one String doAction() method, which is essentially the Supplier<String> you had as a field.

    You would be giving the field away with a getter anyway, so why don't you just provide a method?

    enum SomeEnum {
        CASE1("val1") {
            @Override
            public String doAction() {
                return this.value + "-> action1";
            }
        },
        CASE2("val2") {
            @Override
            public String doAction() {
                return this.value + "-> action2";
            }
        };
    
        String value;
    
        SomeEnum(String value) {
            this.value = value;
        }
    
        public abstract String doAction();
    }