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

欢迎!请参见关于页面了解更多关于这里的工作方式的信息。

+2
头像 Java 互操作

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
头像

看起来与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. 如果还有许多方法,比较类型并尝试选择一个

因此,在用任何参数类型信息过滤可能的方法列表之前,桥梁方法首先被扔掉。这似乎意味着类型过滤应在桥梁方法过滤之前发生。但是我承认我对桥梁方法的理解相当模糊(并且越来越不清晰)。
...