2

I'll try to keep this short. I'm trying to do something like this:

public enum Fruit {
    APPLE("Apple", appleHelper::doAppleThing),
    ORANGE("Orange", orangeHelper::doOrangeThing);

    private String name;
    private Function<String, List<T>> fruitFunction;

    Fruit(String name, Function<String, List<T>> fruitFunction) {
      this.name = name;
      this.fruitFunction = fruitFunction;
    }

    public String getName() {
      return name;
    }

    public <T> List<T> applyFruitFunction(String someString) {
      return fruitFunction.apply(someString);
    }
}

Such that later, I can have a method like

private <T> List<T> doFruitThing(String someString, Fruit fruit) {
    List<T> transformedFruits = fruit.applyFruitFunction(someString);

    if (transformedFruits.isEmpty()) {
        throw new FruitException("There was no fruit of type " + fruit.getName());
    }

    return transformedFruits;
}

There's two problems I'm running into here.

  1. doAppleThing and doOrangeThing are not static methods, and ideally will stay that way, and I can't find any way of creating a local instance of appleHelper and orangeHelper to make the method reference work.
  2. Even if I were to make the methods static, enums can't have Type parameters, so there's no way to have Function<String, List<T>> fruitFunction as a field.

Is there a way this can be done? Or a better approach to this?

EaterOfFromage
  • 351
  • 5
  • 12
  • You want the methods to be non-static, but have the enum initialized with an instance of the method? And the method can't be set from outside the enum? That's exactly what static methods are for. If on the other hand, you want the enum to take instances of their helper class and use that instances methods, then that is exactly what enums are not for. – mypetlion Aug 17 '18 at 20:27
  • maybe related: https://stackoverflow.com/questions/8657608/using-generics-with-collection-of-enum-classes-implementing-same-interface/8677236#8677236 – Ray Tayek Aug 18 '18 at 03:18
  • maybe related: https://stackoverflow.com/questions/22588518/lambda-expression-and-generic-method – Ray Tayek Aug 21 '18 at 03:16

1 Answers1

6

Enum values can have their own method implementations. So I would write this as:

public enum Fruit {
    APPLE("Apple") {
        private final AppleHelper helper = new AppleHelper();

        @Override
        public <T> List<T> applyFruitFunction(String someString) {
            return helper.doAppleThing(someString);
        }
    },

    ORANGE("Orange") {
        private final OrangeHelper helper = new OrangeHelper();

        @Override
        public <T> List<T> applyFruitFunction(String someString) {
            return helper.doOrangeThing(someString);
        }
    };

    private String name;

    Fruit(String name) {
      this.name = name;
    }

    public String getName() {
      return name;
    }

    public abstract <T> List<T> applyFruitFunction(String someString);
}

However, if you get to the point of needing per-instance state for enum instances, the thing you have is less and less an enum and more of just an abstract base class. It might be better to look into a more OO approach, using a factory/flywheel pattern for example, rather than being tied to a pure enum for this sort of thing. (It's hard to tell for sure because the code in the question is obviously just a simplified example.)

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
  • Now that's a solution! And also corrects the misplaced constructor. – ernest_k Aug 17 '18 at 20:27
  • This is a great answer! Thanks. However, since the implementation of doAppleThing and doOrangeThing are quite complex, I want them in a different class (`AppleHelper` and `OrangeHelper`). By making those methods static I can just make the call, but I guess there's still no way of doing it with instance methods. Oh well, thanks! – EaterOfFromage Aug 20 '18 at 13:22
  • @EaterOfFromage: See my latest edit -- I believe that's what you're looking for. – Daniel Pryden Aug 21 '18 at 01:47