2024年Clojure调查! 中分享你的想法。

欢迎!请参阅 关于 页面,了解更多关于它是如何工作的信息。

+2 投票
Description Java Interop

Google Guava 30.1 定义了这两个为 TreeRangeSet 的 addAll 重载

public void TreeRangeSet.addAll(RangeSet)
public default void RangeSet.addAll(Iterable<Range>)

以下代码没有错误或警告地编译

(import [com.google.common.collect Range RangeSet TreeRangeSet])

(defn example []
  (let [container (TreeRangeSet/create)
        temp (TreeRangeSet/create ^Iterable (list (Range/closed 1 2)))]
     (.addAll container ^RangeSet temp)))

然而,调用示例会因运行时错误而失败,错误信息指出 TreeRangeSet 没有实现 Iterable。据我了解,这个错误是因为 Clojure 编译器调用了第二个重载(尽管有类型提示)。经过实验,我相信这是因为在第一个重载对 isBridge 返回 true;Reflector.getMethods 将不返回它,因为第二个重载不是一个桥接方法。

所以有两个问题:有没有办法生成对第一个重载的调用,如果没有,那么是否应该有?

(顺便说一句,在初始化 temp 上面的,我想使用

(let [temp (TreeRangeSet/create ^Iterable [(Range/closed 1 2)])

但这生成了反射警告。我相信这是一个 CLJ-1929 的例子。)

1 答案

+1 投票
Description

看起来很像 https://clojure.atlassian.net/browse/CLJ-1243,addAll(RangeSet ...) 方法定义在一个包私有类 https://github.com/google/guava/blob/v30.1/android/guava/src/com/google/common/collect/AbstractRangeSet.java 中。还有一个接口(https://github.com/google/guava/blob/v30.1/android/guava/src/com/google/common/collect/RangeSet.java),但那里所有方法都没有访问修饰符,因此是私有的(不清楚这会对 Java 继承产生什么影响)

对不起,接口方法是包私有,而不是私有,所以和抽象范围集的addAll方法一样
看起来addAll(RangeSet)方法被javac标记为桥接方法,而clojure.lang.Reflector(在非反射情况下编译时也会进行查找)在找不到其他方法时忽略桥接方法
clojure.lang.Reflector的逻辑基本上是

1. 获取该类的所有方法
2. 删除任何不匹配名称的方法
3. 删除不匹配参数数量的方法
4. 如果剩下任何非桥接方法,删除所有桥接方法
5. 如果只剩下一个方法,那就是方法
6. 如果还有许多方法,比较类型并尝试选择一个

因此,在应用任何类型的参数信息来过滤可能的方法列表之前,桥接方法首先被排除。这似乎表明,类型过滤应该先进行,然后再过滤桥接方法。但我承认我对接口桥接方法的掌握相当松散(并且在渐渐失去准确性)。
...