【问题标题】:How to dynamically and recursively decorate a class in Java/Scala?如何在 Java/Scala 中动态和递归地装饰一个类?
【发布时间】:2016-10-09 17:00:32
【问题描述】:

假设我在为梦想建模:

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

我的通用方法将尝试设置一个新的Decorator,将SimpleDream 包装到LucidDreaminner 字段中,这样做(使用Field.set)将引发异常(java.lang.IllegalArgumentException: Can not set final X field Y to Z)。

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

附言
我实际上使用的是 Scala,所以 (a) 请原谅我的 Java 代码,以及 (b) 如果这只能通过 Scala 以某种方式实现,我也想知道这一点。

【问题讨论】:

  • 请原谅我问一个显而易见的问题:你不能将 LucidDream 更改为具有“Dream”类型的“内在”吗?即“类 LucidDream 实现 Dream{ Dream inninter;}
  • 好吧,不幸的是,没有。
  • 如果有人感兴趣,我的实际用例是通过 AST 调试日志记录(在评估树中每个表达式之前和之后进行调试)。

标签: java scala reflection


【解决方案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)

但是,这并非微不足道(这会使维护和代码部署变得复杂),所以只有当我真的别无选择时,我才会求助于它,即我真的需要这种水平的泛化,而且我真的无法改变到“梦想内心”....

【讨论】:

    【解决方案2】:

    你是这个意思吗?

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

    【讨论】:

    • 不,我不是那个意思。我希望将LucidDream.inner 定义为SimpleDream,这就是重点。
    猜你喜欢
    • 1970-01-01
    • 2020-02-03
    • 1970-01-01
    • 1970-01-01
    • 2011-02-02
    • 2019-09-14
    • 2012-06-01
    • 2016-03-06
    • 1970-01-01
    相关资源
    最近更新 更多