如何动态和递归地装饰Java / Scala中的类?

时间:2016-10-09 17:00:33

标签: java scala reflection

说我在塑造梦想:

public interface Dream {

  void start();
}

public class SimpleDream implements Dream {
 // implementation here...
}

public class Inception implements Dream {

  Dream inner;

 // implementation here...
}

现在说我有这些梦想的装饰师课程:

public class Decorator implements Dream {

  Dream decorated;

  public void start() {

    // do something else
    decorated.start();
  }
}

我希望能够编写一个方法,该方法将采用任何Dream实例并使用Decorator 递归(字段)来修饰它。我能用反射编写这样的方法,但在尝试装饰Dream子类型的字段时遇到了问题:

public class LucidDream implements Dream {

  SimpleDream inner;

 // implementation here...
}

我的通用方法会尝试设置一个新的DecoratorSimpleDream包裹到inner的{​​{1}}字段中,然后执行此操作(使用LucidDream)将抛出异常(Field.set)。

我尝试使用代理,但它没有用。

P.S。
我实际上正在使用Scala,所以(a)原谅我的Java代码,(b)如果只有Scala可以实现这一点,我也想知道。

2 个答案:

答案 0 :(得分:1)

不幸的是,你说你不能改变LucidDream.inner

class LucidDream implements Dream{
  SimpleDream inner; // CANNOT be changed to "Dream"
}

在这种情况下,据我所知,生活变得更加复杂,唯一的解决方法是动态扩展SimpleDream ,使用字节码增强库,如cglib。 请参阅此处的讨论(忽略他们正在讨论抽象类的事实 - 主要是动态扩展类而不是接口):

Alternatives to java.lang.reflect.Proxy for creating proxies of abstract classes (rather than interfaces)

然而,这不是微不足道的(这使得维护和代码部署变得复杂)所以我只有在我真的别无选择时才会求助于它,即我真的需要这种泛化水平,而且我真的无法&# 39; t改为"梦想内心" ....

答案 1 :(得分:0)

你是说这个吗?

public class DreamDecorator() implements Dream {
  Dream decorated;
  public DreamDecorator(Dream decorated) {
     this.decorated = decorated;
  }

  public void start() {

    // do something else
    decorated.start();
  }
}

public class LucidDream(Dream dream) implements Dream {
  Dream dream;
  public LucidDream(Dream dream) {
     this.dream= dream;
  }

}

你可以通过这个将装饰简单的梦想传递给清醒的梦想

new LucidDream(new DreamDecorator(new SimpleDream()))

或动态使用反射,例如:

Class<?> sdClazz = Class.forName(SimpleDream.class.getName());
Constructor<?> sdCtor = sdClazz.getConstructor();
SimpleDream sd = (SimpleDream) sdCtor.newInstance();

Class<?> ddClazz = Class.forName(DreamDecorator.class.getName());
Constructor<?> ddCtor = ddClazz.getConstructor(Dream.class);
DreamDecorator dd = (DreamDecorator) ddCtor.newInstance(new Object[] { sd });

Class<?> clazz = Class.forName(LucidDream.class.getName());
Constructor<?> ctor = clazz.getConstructor(Dream.class);
LucidDream lucidDream = (LucidDream) ctor.newInstance(new Object[] { dd});