I sometimes run into this scenario and not sure if I'm using a bad approach or I just don't know how to solve it.
Let's say I have two classes and two beans like this:
public class BeanOne {
public void methodBeanOne() {
//...
}
}
public class BeanTwo {
public void methodBeanTwo() {
//...
}
}
public class ClassOne {
private BeanOne bean;
public ClassOne(BeanOne bean) {
this.bean = bean;
}
public void methodclassOne() {
bean.methodBeanOne();
}
}
public class ClassTwo {
private BeanTwo bean;
public ClassTwo(BeanTwo bean) {
this.bean = bean;
}
public void methodClassTwo() {
bean.methodBeanTwo();
}
}
I want to make a generic abstract class , so I can extract some logic from ClassOne
and ClassTwo
to it, and an abstract bean with common methods as well:
public abstract class AbstractBean {
public void commonMethod() {
//...
}
}
public class BeanOne extends AbstractBean {
public void methodBeanOne() {
//...
}
}
public class BeanTwo extends AbstractBean {
public void methodBeanTwo() {
//...
}
}
public abstract class AbstractClass<T extends AbstractBean> {
protected T bean;
public AbstractClass(T bean) {
this.bean = bean;
}
public void commonClassMethod(){
bean.commonMethod();
}
}
public class ClassOne extends AbstractClass<BeanOne> {
public ClassOne(BeanOne bean) {
super(bean);
}
public void methodclassOne() {
bean.methodBeanOne();
}
}
public class ClassTwo extends AbstractClass<BeanTwo> {
public ClassTwo(BeanTwo bean) {
super(bean);
}
public void methodClassTwo() {
bean.methodBeanTwo();
}
}
So far, so good.
The next step would be to create a factory to get one implementation based on an enum
for instance, and here is where I start getting errors:
public class ClassFactory {
public enum MyEnum {
ONE, TWO;
}
private ClassFactory() {
}
public static AbstractClass newInstance(MyEnum value, AbstractBean bean) {
switch(value){
case ONE:
return new ClassOne(bean);
case TWO:
return new ClassTwo(bean);
default:
throw new IllegalArgumentException();
}
}
}
This gives the following compilation errors:
The constructor ClassOne(AbstractBean) is undefined
The constructor ClassTwo(AbstractBean) is undefined
I've also tried:
public class ClassFactory {
public enum MyEnum {
ONE, TWO;
}
private ClassFactory() {
}
public static <T extends AbstractBean> AbstractClass<T> newInstance(MyEnum value, T bean) {
switch(value){
case ONE:
return new ClassOne(bean);
case TWO:
return new ClassTwo(bean);
default:
throw new IllegalArgumentException();
}
}
}
But then I get:
Type mismatch: cannot convert from ClassOne to AbstractClass<T>
Type mismatch: cannot convert from ClassTwo to AbstractClass<T>
And I'm pretty much stuck there. I think I understand this error, but then, is it possible to create such a factory class trying to avoid castings?
I've also checked this post, but can't fully understand how it'd help me.
EDIT: Visitor Pattern
Ok, so I've tried the Visitor Pattern described in the previos post:
public interface Visitor<T> {
T visit(BeanOne bean);
T visit(BeanTwo bean);
}
public abstract class AbstractBean {
public void commonMethod() {
// ...
}
public abstract <T> T accept(Visitor<T> visitor);
}
public class BeanOne extends AbstractBean {
public void methodBeanOne() {
// ...
}
@Override
public <T> T accept(Visitor<T> visitor) {
return visitor.visit(this);
}
}
public class BeanTwo extends AbstractBean {
public void methodBeanTwo() {
// ...
}
@Override
public <T> T accept(Visitor<T> visitor) {
return visitor.visit(this);
}
}
public class ClassFactory {
private ClassFactory() {
}
public static AbstractClass<? extends AbstractBean> newInstance(AbstractBean bean) {
return bean.accept(new AbstractClassVisitor());
}
}
public class AbstractClassVisitor implements Visitor<AbstractClass<? extends AbstractBean>> {
@Override
public AbstractClass<? extends AbstractBean> visit(BeanOne bean) {
return ClassFactory.newInstance(bean);
}
@Override
public AbstractClass<? extends AbstractBean> visit(BeanTwo bean) {
return ClassFactory.newInstance(bean);
}
}
But using it like this:
AbstractBean bean = new BeanOne();
AbstractClass<? extends AbstractBean> clazz = ClassFactory.newInstance(bean);
clazz.commonClassMethod();
I'm getting the following exception:
Exception in thread "main" java.lang.StackOverflowError
at test.AbstractClassVisitor.<init>(AbstractClassVisitor.java:3)
at test.ClassFactory.newInstance(ClassFactory.java:9)
at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:7)
at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:1)
at test.BeanOne.accept(BeanOne.java:10)
at test.ClassFactory.newInstance(ClassFactory.java:9)
at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:7)
at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:1)
at test.BeanOne.accept(BeanOne.java:10)
at test.ClassFactory.newInstance(ClassFactory.java:9)
at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:7)
at test.AbstractClassVisitor.visit(AbstractClassVisitor.java:1)
at test.BeanOne.accept(BeanOne.java:10)
...
I can see why this is happening, am I missing something?