6

I tried to implement an Enum styled factory pattern as inner Enum, but it didn't work. Is there any solution without separating inner Enum into a new file? In other words, is it possible of inner Enum styled factory pattern?

The code is below.

public class SampleParent {

    private class InnerChild { }
    private class InnerChildA extends InnerChild { }
    private class InnerChildB extends InnerChild { }
    private class InnerChildC extends InnerChild { }

    enum InnerChildEnum {
        CHILD_A {
            @Override
                public InnerChild getInstance() {
                    return new InnerChildA();  // compile error
                }
        },
        CHILD_B {
            @Override
                public InnerChild getInstance() {
                    return new SampleParent.InnerChildB();  // compile error
                }
        },
        CHILD_C {
            @Override
                public InnerChild getInstance() {
                    return SampleParent.new InnerChildC();  // compile error
                }
        },
        ;

        public abstract InnerChild getInstance();
    }

    private static class InnerChildFactoryEnumStyled {
        public static InnerChild getInnerChild(InnerChildEnum child) {
            return child.getInstance();
        }
    }


    public static void main(String[] args) {

        // I want to write this way
        InnerChild child = InnerChildFactoryEnumStyled.getInnerChild(InnerChildEnum.CHILD_A);
    }
}

The compile error message is below

$ javac SampleParent.java 
SampleParent.java:12: error: non-static variable this cannot be referenced from a static context
                return new InnerChildA();
                       ^
SampleParent.java:18: error: non-static variable this cannot be referenced from a static context
                return new SampleParent.InnerChildB();
                       ^
SampleParent.java:24: error: cannot find symbol
                return SampleParent.new InnerChildC();
                       ^
  symbol: variable SampleParent
3 errors
forte916
  • 113
  • 1
  • 7
  • You could simply make the InnerChildXX classes static. It wouldn't be a problem based on your example. – assylias Mar 23 '17 at 14:13
  • The answer is here: http://stackoverflow.com/questions/663834/in-java-are-enum-types-inside-a-class-static – Markus Benko Mar 23 '17 at 14:15
  • Thanks, as I mentioned below, unfortunately, the actual inner class is a child of AsyncTask and cannot be static. – forte916 Mar 23 '17 at 14:29

3 Answers3

5

Your inner classes are not static so they must refer to an instance of the enclosing class SampleParent. Change your class declarations to

private static class InnerChild { }
private static class InnerChildA extends InnerChild { }
private static class InnerChildB extends InnerChild { }
private static class InnerChildC extends InnerChild { }

and you can e. g. return new InnerChildA(); in your enum.

Stefan Warminski
  • 1,845
  • 1
  • 9
  • 18
  • I saw you posted your answer before I did, so I deleted my post. – fps Mar 23 '17 at 14:18
  • Thanks for the solution. It is make sense to change static. but unfortunately, the actual inner class is a child of AsyncTask and the child class has non-static members. Your answer is very helpful. – forte916 Mar 23 '17 at 14:27
3

To instantiate a no static inner class(InnerChildA, InnerChildB, etc...), you need to qualify the instantiation with an instance of the enclosing type (SampleParent). You have to do something like that : new ExternalClass().new InternalClass()

In your case you could declare and instance a static SampleParent instance in the InnerChildEnum enum and reuse it in each implemented getInstance() method of the enum values declaration.

private class InnerChild {
}

private class InnerChildA extends InnerChild {
}

private class InnerChildB extends InnerChild {
}

private class InnerChildC extends InnerChild {
}

enum InnerChildEnum {

    CHILD_A {
        @Override
        public InnerChild getInstance() {
            return sampleParent.new InnerChildA();
        }
    },
    CHILD_B {
        @Override
        public InnerChild getInstance() {

            return sampleParent.new InnerChildB(); // compile error
        }
    },
    CHILD_C {
        @Override
        public InnerChild getInstance() {
            return sampleParent.new InnerChildC();
        }
    };

    private static SampleParent sampleParent = new SampleParent();

    public abstract InnerChild getInstance();
}
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • Thanks for the post, I will try and report after. – forte916 Mar 23 '17 at 14:25
  • The first problem is solved. Unfortunately, I faced another problem. The created instance in Enum and original instance is different object. So, if SampleParent have non-instance members, the created instance cannot refer the original instance's member. But it is second problem. I try to solve. – forte916 Mar 24 '17 at 07:42
  • I think that you would say "if SampleParent have instance members...". To solve your problem you should share this instance between these two classes and even maybe ensure that you have not more than one `SampleParent` instance. Easy way (sharing the instance) : replace `private static SampleParent sampleParent = new SampleParent();` by `public static SampleParent sampleParent = new SampleParent();` Now you can refer the same object everywhere as it is public – davidxxx Mar 24 '17 at 08:46
  • About the way to have not more than a single instance at runtime, you could use the classic singleton pattern or dependency injection. – davidxxx Mar 24 '17 at 08:48
1

The improvements code is here, in case.

public class SampleParent {

    private String foo = "foo";

    private abstract class InnerChild {
        public abstract void doSomething();
    }
    private class InnerChildA extends InnerChild {
        public void doSomething() {
            System.out.println(foo);  // refer sampleParent.foo, not original's foo
        }
    }
    private class InnerChildB extends InnerChild {
        public void doSomething() {
            System.out.println(foo);  // refer sampleParent.foo, not original's foo
        }
    }
    private class InnerChildC extends InnerChild {
        public void doSomething() {
            System.out.println(outer().foo);  // refer sampleParent.foo, not original's foo
        }
    }

    enum InnerChildEnum {
        CHILD_A {
            @Override
            public InnerChild getInstance() {
                return sampleParent.new InnerChildA();
            }
        },
        CHILD_B {
            @Override
            public InnerChild getInstance() {
                return sampleParent.new InnerChildB();
            }
        },
        CHILD_C {
            @Override
            public InnerChild getInstance() {
                return sampleParent.new InnerChildC();
            }
        },
        ;

        private static SampleParent sampleParent = new SampleParent();
        public abstract InnerChild getInstance();
    }

    private static class InnerChildFactoryEnumStyled {
        public static InnerChild getInnerChild(InnerChildEnum child) {
            return child.getInstance();
        }
    }


    public static void main(String[] args) {
        System.out.println("Hello World.");

        // I want to write this way
        InnerChild childA = InnerChildFactoryEnumStyled.getInnerChild(InnerChildEnum.CHILD_A);
        childA.doSomething();

        InnerChild childB = InnerChildFactoryEnumStyled.getInnerChild(InnerChildEnum.CHILD_B);
        childB.doSomething();

        InnerChild childC = InnerChildFactoryEnumStyled.getInnerChild(InnerChildEnum.CHILD_C);
        childC.doSomething();
    }

    public SampleParent outer() {
        return SampleParent.this;
    }
}
forte916
  • 113
  • 1
  • 7