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继承产生什么影响)

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

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

因此,在将任何参数类型信息用于过滤可能的方法列表之前,桥接方法首先会被丢弃。看起来可能应该首先进行类型筛选,然后是桥接方法筛选。但是我承认我对桥接方法的理解相当模糊(以及下滑)。
...