user18076598
user18076598

Reputation:

Workaround implementation for "enum inheritance" in Java?

Preface: I've already researched why "enum inheritance" is illegal in Java.

My problem is the following: given a class Recipe, I want its property category to be a list of constant values like APPETIZER, BREAKFAST, DESSERT, MAIN_COURSE, SOUP - I logically use enums for this.

My question then is: if I wanted each of this enums to have "children of their own" (for example: SWEET and SAVORY for BREAKFAST, or CAKE, MUFFIN and BISCUITS for DESSERT), so that:

  1. Specifying the subcategory ("child enum") is mandatory (e.g. myRecipe.setCategory(DESSERT) should raise an exception);
  2. Using a "child enum" from a different "family" is forbidden (e.g. SOUP.BISCUITS should raise an exception);
  3. I should be able to access the "child enum" through dot notation (e.g. myRecipe.setCategory(DESSERT.CAKE)) - or other similar "lightweight" syntax.

I haven't been able to come up with any clean solution in Java to fit all three requisites.

Is there any "esoteric" design pattern for this? How would you implement this?

Upvotes: 3

Views: 133

Answers (2)

Anuj
Anuj

Reputation: 1665

Simple Design

Create a class Category. Inside Category, declare all the enum classes.

public class Category
{
    public enum APPETIZER
    {
    }

    public enum BREAKFAST
    {
        SWEET,
        SAVORY
    }

    public enum DESSERT
    {
        CAKE,
        MUFFIN,
        BISCUITS
    }

    public enum MAIN_COURSE
    {
    }
}

Inside the Recipe class, category should be of type DESSERT. I have static imported Category class.

public class Recipe
{
    DESSERT category;

    public void setCategory(DESSERT category)
    {
        this.category = category;
    }

    public static void main(String[] args)
    {
        Recipe myRecipe = new Recipe();

        myRecipe.setCategory(DESSERT.BISCUITS);

        // statements below give compile time errors
        // myRecipe.setCategory(DESSERT);
        // myRecipe.setCategory(BREAKFAST.SWEET);
    }
}

Improvement

Convert Category into a marker interface. All the categories such as DESSERT, BREAKFAST, etc. should implement Category.

interface Category {}

enum APPETIZER implements Category 
{
}

enum BREAKFAST implements Category
{
    SWEET,
    SAVORY
}

enum DESSERT implements Category
{
    CAKE,
    MUFFIN,
    BISCUITS
}

enum MAIN_COURSE implements Category
{
}

Make Recipe generic.

public class Recipe <T extends Category>
{
     T category;

    public void setCategory(T category)
    {
        this.category = category;
    }

    public static void main(String[] args)
    {
        Recipe<DESSERT> myRecipe = new Recipe<>();

        myRecipe.setCategory(DESSERT.BISCUITS);

        // statements below give compile time errors
        // myRecipe.setCategory(DESSERT);
        // myRecipe.setCategory(BREAKFAST.SWEET);
    }
}

These are not design patterns. They are self implementation.

Upvotes: 1

tgdavies
tgdavies

Reputation: 11494

You can do this:

class Recipe {
    private final Meal meal;
    private final MealCategory category;

    public <T extends Meal> Recipe(T meal, MealCategory<T> category) {
        this.meal = meal;
        this.category = category;
    }
}
abstract class Meal {}
class Breakfast extends Meal {}
class Dinner extends Meal {}

class MealCategory<T extends Meal> {}

class Cereal extends MealCategory<Breakfast> {}
class Meat extends MealCategory<Dinner> {}

public class Test {

    public static void main(String[] args) {
        Recipe r = new Recipe(new Breakfast(), new Cereal());
        Recipe r2 = new Recipe(new Breakfast(), new Meat()); // compile time error
    }
}

Upvotes: 1

Related Questions