简体   繁体   中英

How to dynamically and recursively decorate a class in Java/Scala?

Say I'm modeling dreams:

public interface Dream {

  void start();
}

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

public class Inception implements Dream {

  Dream inner;

 // implementation here...
}

Now say I have a decorator class for these dreams:

public class Decorator implements Dream {

  Dream decorated;

  public void start() {

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

I want to be able to write a method that'll take any Dream instance and decorate it with Decorator recursively (fields as well). I was able to write such a method with reflection, but I encountered a problem when trying to decorate a field of a sub-type of Dream :

public class LucidDream implements Dream {

  SimpleDream inner;

 // implementation here...
}

My generic method will try to set a new Decorator wrapping the SimpleDream into the inner field of LucidDream , and doing so (with Field.set ) will throw an exception ( java.lang.IllegalArgumentException: Can not set final X field Y to Z ).

I tried using proxies but it didn't work.

PS
I'm actually using Scala, so (a) forgive me for my Java code, and (b) if this is somehow achievable only with Scala, I'd like to know that as well.

Unfortunately, you said you can't change LucidDream.inner

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

In that case, to the best of my knowledge, life becomes more complicated, and the only workaround would be to dynamically extend SimpleDream , using byte code enhancement libraries such as cglib. See discussion here (ignore the fact they're discussing an abstract class - the main point is dynamically extending a class rather than interface):

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

However, this is not trivial (this complicates maintenance as well as code deployment) so I'd resort to it only if I really had no choice, namely I really needed this level of generalization, and I really couldn't change to "Dream inner"....

Do you mean this?

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;
  }

}

you can pass the decorated simple dream to lucid dream by doing this

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

or dynamically using reflection, example:

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});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM