【问题标题】:Function pointer to String method in JavaJava中指向String方法的函数指针
【发布时间】:2019-01-08 08:59:04
【问题描述】:

我不了解 lambda 的一些内容。

String s = "Hello World";       
Function<Integer, String> f = s::substring;
s = null;
System.out.println(f.apply(5));

如果s = null,为什么f.apply 方法仍然有效。毕竟String对象应该被GC删除,因为没有指向该对象的指针。

还有一件事,为什么我这里不需要return 语句?

Function<Integer, String> f = t -> t + "";

【问题讨论】:

  • 绝对有一个指向对象的指针。 f 成为一个对象,其中包含指向 "Hello world" 的指针。 a::foo 评估 a 并存储对其结果的引用。
  • 附带一个问题,s 的初始引用存在于字符串常量池中,因此永远不会被垃圾回收(除非包含类)
  • 尝试每个问题只问 一个 问题。
  • @LouisWasserman “避免在 cmets 中回答问题”。目前这个问题在 HNQ 上,评论有 3 个赞,这(肯定)会给访问者造成错误的印象。

标签: java lambda


【解决方案1】:

JLS, Section 15.13.3 描述了方法引用的运行时评估。

方法引用表达式求值的时间比 lambda 表达式更复杂(第 15.27.4 节)。当方法引用表达式在 :: 分隔符之前有一个表达式(而不是类型)时,会立即计算该子表达式。 求值结果一直保存,直到调用对应的函数接口类型的方法;此时,结果将用作调用的目标引用。这意味着 :: 分隔符之前的表达式仅在程序遇到方法引用表达式时才被计算,并且不会在函数接口类型的后续调用中重新计算。

(我的粗体强调)

基本上,对于方法引用的s 的引用被存储以供以后执行。在这里,字符串"Hello World" 被保存以供以后执行方法引用。因此,即使在方法引用声明之后,将s设置为null,但在执行Function之前,它仍然会使用字符串"Hello World"

将某些东西设置为null 并不能保证垃圾收集器会收集它,也不能保证它会立即被收集。

另外,这里的方法引用中还有一个引用,所以这里根本不会被垃圾回收。

最后,lambda 表达式主体有两种形式:一个表达式和一个带有(可能)返回语句的语句块。与

Function<Integer, String> f = t -> t + "";

这是一种表达方式。块语句等效为:

Function<Integer, String> f = t -> { return t + "";};

【讨论】:

  • 在我的示例中,JLS 表示 f 正在调用该方法并保存结果,当我使用 get 函数时,他返回了 ruslt。你说因为 f 是对方法的引用所以对象(“Hello World”)仍然存储在内存中,你说一件事他说另一件事还是我不明白?
  • 我不知道您指的JLS中的哪个示例,但是Java在遇到方法引用时不会调用该方法(此处为substring)。稍后调用函数方法时会调用它,这里是apply
  • 所以你是说java会因为指向方法的指针(这里是子字符串)而保存对象(这里是String)?
  • 是的。遇到方法引用时,会评估 s 并保存对 "Hello World" 的单独引用以供以后执行。
  • 所以间接引用“Hello World”?
【解决方案2】:

让我们将该方法引用转换为 lambda,看看会发生什么:

String s = "Hello World";
Function<Integer, String> f = i -> s.substring(i); // Doesn't compile!
s = null;
System.out.println(f.apply(5));

上面没有编译,因为s 在 lambda 之外被更改,所以它实际上不是最终的。因此,我们可以推断,使用方法引用会在实际使用之前缓存s 的值。

见:Is method reference caching a good idea in Java 8?

【讨论】:

  • 我不认为这是正确的...如果您删除最后一个 s = null 并反编译生成的类,您会看到它们 both (lambda 和方法引用)尝试缓存该值 - 对于 lambda 来说这是一个不同的故事,因为“匿名类”的规则适用
  • 我也是这么想的,我尝试使用 annonymos 类而不是方法引用,但它不起作用,因为我需要将“s”更改为 final。这种推断有利于思考,但很多时候我在编程中推断出一些东西,有时我是不对的。
  • @user5327287 完全正确。使用 lambda 就像使用匿名内部类,但使用方法引用甚至不接近。更多阅读stackoverflow.com/a/33053161/1059372,老实说,这是那个的副本
【解决方案3】:

还有一件事,为什么我这里不需要return 语句?

当只指定一个 lambda 语句时,它的值会自动从 lambda 中返回。

Function<Integer, String> f = s::substring;

Java 与 pass-by-value. 一起工作,所以,f 是一个参考副本。

【讨论】:

  • 我一直认为它是参考而不是价值。
  • @user5327287 引用的证据是,在方法参数void c(String s) {s = "xx"; } 中,你主要喜欢String s = "bb"; c(s); System.out.println(s);,该值仍然是'bb',因为非引用已更改。
猜你喜欢
  • 2014-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-26
  • 1970-01-01
相关资源
最近更新 更多