2024 年 Clojure 状态调查!中分享您的想法。

欢迎!请查阅关于页面,了解更多有关本网站工作方式的信息。

+2
Clojure
closed

使用 java 方法引用可以实现以下功能(以下代码摘自 java.time.LocalDate)

public static LocalDate parse(CharSequence text, DateTimeFormatter formatter) {
    return formatter.parse(text, LocalDate::from);
}

parse 方法的第二个参数是 TemporalQuery - 一个接受单一 TemporalAccessor 作为参数的功能式接口。在这个例子中,没有传递 TemporalQuery 实例,而是传递了方法引用,该引用与方法签名相匹配,即接受单一 TemporalAccessor 作为参数。

尝试在 clojure 中做类似的事情

(^[CharSequence TemporalQuery] DateTimeFormatter/parse formatter "20200202" java.time.LocalDate/from)

会导致以下结果

Execution error (ClassCastException) at user/eval2266 (form-init14137309762912529150.clj:1).

class user$eval2266$invokeLocalDate_from2268 无法转换为 class java.time.temporal.TemporalQuery (user$eval2266$invokeLocalDate_from2268 是在未命名的模块 clojure.lang.DynamicClassLoader @37e99783 中;java.time.temporal.TemporalQuery 是在模块 java.base 中,加载器为 'bootstrap')

您认为这是 bug 还是故意为之?如果是故意为之,关于 clojure 方法值与 java 方法引用相比的一些文档将很有用。

附带备注关闭: 在 Clojure 1.12.0-alpha12 中发布
截至 Clojure 1.12.0-alpha12,Clojure 函数将隐式转换为必要的功能式接口

(^[CharSequence TemporalQuery] DateTimeFormatter/.parse (DateTimeFormatter/ofPattern "yyyyMMdd") "20200202" java.time.LocalDate/from)

2 个回答

+3

这将在下个 alpha 版本中的新功能接口强制转换中工作。

在基本层面,Clojure IFns 将隐式转换为适当类型的接口(或者你可以在 let 绑定中请求显式强制转换)。这是通过 invokedynamic 实现的,实际上是与 Java lambda 编译相同的机制(只是我们将使用一组固定的适配器目标,而不是动态生成合成的 lambda 方法)。

新的方法值将作为 IFns 实现,因此可以自动与之一致,但正如你所想象的,将一个 Java 方法包装成 Ifn,然后再将其转换回方法引用并不是最优的。幸运的是,我们可以在编译器中识别这种情况,并直接对原始方法进行适应。

因此,它即将到来!这是 1.12 的一部分。

太棒了!谢谢 Alex
0

在 Clojure 中,方法值本身就是值。这是创建一个使用 Java 方法正确重载的 lambda 函数的语法糖。

在 Java 中,方法引用是一种创建特定上下文中正确事物的语法糖。你不能 仅仅 获取 LocalDate::from 的值 - 你必须将其放入 Java 编译器能够了解你确切需求的上下文中。

你的例子更多的是关于 Java,而不是 Clojure。Java 编译器会找到所有的签名,并使它们兼容,从而能够使用 ...::from

由于你只是调用 parse 函数,所以使用 when 而不是其他情况并不合理。

(^[CharSequence TemporalQuery] DateTimeFormatter/parse formatter x y)

当 ...

(.parse ^DateTimeFormatter formatter ^CharSequence x ^TemporalQuery y)

然而,这对您的案例没有帮助,因为 LocalDate/from 不是 TemporalQuery 的实例。在 Clojure 中,LocalDate/from 是一个普通的 Clojure 函数。即使您使用 ^[TemporalAccessor] LocalDate/from,它仍然是一个普通的 Clojure 函数,只是指向 那个 .../from 方法(在此不重要,因为 LocalDate 中只有一个此类方法,但如果有多个重载时则很重要)。该函数的签名仍然是 [Object]。(即使签名是 [TemporalQuery],我也怀疑这能帮助,因为此处不会调用 Java 编译器,但我可能错了。)

综上所述,我 pretty sure 随着目前的状况,您要实现您想要的功能的唯一方法是使用古老的 reify

(let [formatter java.time.format.DateTimeFormatter/BASIC_ISO_DATE]
  (.parse formatter
          "20200202"
          (reify java.time.temporal.TemporalQuery
            (queryFrom [_ ^java.time.temporal.TemporalAccessor temporal]
              (java.time.LocalDate/from temporal)))))
看起来我的其他评论——这个功能实际上即将推出。
...