【问题标题】:Java 8 - how to access object and method encapsulated as lambdaJava 8 - 如何访问封装为 lambda 的对象和方法
【发布时间】:2019-05-18 00:16:40
【问题描述】:

在 Java 中,您可以将“对对象的方法调用”“捕获”为 Runnable,如下例所示。

稍后,如果可以访问这个 Runnable 实例,是否可以实际访问“捕获的”对象和被调用方法的方法参数(如果可能,这可能需要通过反射来完成)。

例如:

class SomePrintingClass {
  public void print(String myText) {
    System.out.println(myText);

  }
}


public class HowToAccess {
  public static void main(String[] args) throws Exception {
    final String myText = "How to access this?";

    final SomePrintingClass printer = new SomePrintingClass();

    Runnable r = () -> printer.print(myText); // capture as Runnable

    inspect(r);
  }


  private static void inspect(Runnable runnable) {
    // I have reference only to runnable... can I access "printer" here

  }


}

是否可以在“inspect”方法中访问(可能通过反射)“printer”对象和作为参数传递的“myText”?

【问题讨论】:

  • 您是否考虑过使用比Runnable 更具体的接口,以及返回您实际需要的方法?您将要使用的反射代码类型可能不太适合生产代码,因为您正在对传入的 Runnable 强制执行隐藏要求(即作为 lambda 并捕获了一些变量)。
  • @DidierL:如果你的意思是SomePrintingClass 可以实现这个“更具体”的接口,我就做不到。假设我无法更改 SomePrintingClass 的定义中的任何内容,我只能依靠我在 inspect() 方法中得到的内容。但是,我可能会使用 Runnable 以外的其他接口(创建我自己的) - 可以更改 inspect 方法的签名,但这可能不会简单地解决问题。关于“隐藏的要求”,我同意,但实际上在这种情况下,有很高的机会访问对象和参数是可以的。我不需要 100% 确定。
  • 是的,我说的是使用比Runnable 更具体的东西。如果你这样做,你就不需要任何反思。
  • 题主和正文不一致:题主问的是“对象和方法”,而正文问的是“对象和方法的参数”,这完全不一样.究竟是什么意思?
  • @Hans-PeterStörr:有权访问 Method 将授予访问其参数的权限。这就是我问方法的原因。不过,这只是一个标题。描述中描述了问题的意图。访问参数的值是最终的意图,但如果我们超越这一点,那就更好了。

标签: java reflection lambda java-8


【解决方案1】:

这是可能的,因为捕获的引用被翻译成可运行的字段(与所有匿名类一样)。但是名称会不一致。

我通过测试发现需要将myText设为非final,否则会被视为compile time constant并内联(并且不能作为字段访问):

private static void inspect(Runnable runnable) throws Exception {       
    for(Field f : runnable.getClass().getDeclaredFields()) {
        f.setAccessible(true);
        System.out.println("name: " + f.getName());
        Object o = f.get(runnable);
        System.out.println("value: " + o);
        System.out.println("class: " + o.getClass());
        System.out.println();
    }
}

打印:

name: arg$1
value: test.SomePrintingClass@1fb3ebeb
class: class test.SomePrintingClass

name: arg$2
value: How to access this?
class: class java.lang.String

【讨论】:

  • 正如我所料,一定有办法做到这一点:)...也感谢您使用参数解决“最终”-“非最终”。公认。干杯。
  • 好吧,要么,非final,要么不立即初始化,例如final String myText; myText = "How to access this?"; 不是编译时常量(虽然这不是禁止内联,但当前编译器不会发生这种情况,而对于编译时常量,内联是强制性的)。
  • 虽然有趣的是可以这样做,但应该注意的是,这依赖于特定于实现的行为(未由规范定义),因此它可能会在不同的 JDK 版本中中断。
  • 除了依赖于特定于实现的行为,正如 Stuart 所说,很明显(但显然不是)您正在访问您无法控制的类的私有状态,这应该是你做错了什么的大红色警告信号,也许你应该寻找另一种解决方案。 (也有可能闯入别人的房子偷东西,但你可以,并不意味着你应该这样做。)
【解决方案2】:

使用反射,无法获取局部变量和方法参数值。相反,您可以使用 AOP 来拦截方法调用并检查参数。

【讨论】:

    【解决方案3】:

    检查您是否想要以下代码,其中我已将可运行对象传递给您的检查方法中的 Thread 对象。

        class SomePrintingClass {
              public void print(String myText) {
                System.out.println(myText);
    
              }
            }
    
    
            public class HowToAccess {
              public static void main(String[] args) throws Exception {
                final String myText = "How to access this?";
    
                final SomePrintingClass printer = new SomePrintingClass();
    
                Runnable r = () -> printer.print(myText); // capture as Runnable
    
                inspect(r);
              }
    
    
              private static void inspect(Runnable runnable) {
                Thread t = new Thread(runnable);
                t.start();
    
              }
    
    
            }
    

    输出将是:

    如何访问这个?

    【讨论】:

    • 我不想执行该方法。我想获得对“打印机”的引用。
    • 好的。然后正如@Kayaman 所述,您将无法在您的检查方法中访问打印机。
    猜你喜欢
    • 1970-01-01
    • 2020-11-24
    • 1970-01-01
    • 2014-07-13
    • 2014-11-05
    • 1970-01-01
    • 1970-01-01
    • 2016-09-23
    • 1970-01-01
    相关资源
    最近更新 更多