2024 年 Clojure 调查问卷! 中分享您的想法。

欢迎!请参阅 关于 页面以了解如何使用本站的一些更多信息。

+2 表扬票
Java 互操作

Google Guava 30.1 定义了这两个 `addAll` 重载用于 `TreeRangeSet`

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

看起来很像是 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. 如果剩下很多方法,比较类型并尝试选择一个

所以桥梁方法在基于任何参数类型信息过滤可能的方法之前被排除在外。这可能意味着类型过滤应该在桥梁方法过滤之前发生。但我要承认,我对桥梁方法的掌握非常松散
...