Welcome! Please see the About page for a little more info on how this works.

+2 votes
in Java Interop by

Google Guava 30.1 defines these two addAll overloads for TreeRangeSet:

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

The following compiles without error or warning:

(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)))

However, calling example fails with a runtime error stating that TreeRangeSet does not implement Iterable. As I understand it, this error happens because the Clojure compiler has emitted a call to the second overload (despite the type hint). After some experimenting, I believe this happens because the first overload returns true for isBridge; Reflector.getMethods will not return it since the second overload is not a bridge method.

So two questions: is there a way to generate a call to the first overload, and, if not, should there be?

(By the way, in initializing temp above, I wanted to use:

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

but this generates a reflection warning. I belive it is an example of CLJ-1929.)

1 Answer

+1 vote
by

looks a lot like https://clojure.atlassian.net/browse/CLJ-1243, the addAll(RangeSet ...) method is defined in a package private class https://github.com/google/guava/blob/v30.1/android/guava/src/com/google/common/collect/AbstractRangeSet.java. There is also an interface(https://github.com/google/guava/blob/v30.1/android/guava/src/com/google/common/collect/RangeSet.java), but all the methods there lack access modifiers, so are private (not sure what that does to java inheritance)

by
sorry, the interface methods are package private, not private, so just like the abstract range set addAll, method
by
it looks like the addAll(RangeSet) method is tagged as a bridge method by javac, and clojure.lang.Reflector (which also does the lookup when compiling in non-reflective cases) ignores bridge methods unless no other methods are found
by
The logic for clojure.lang.Reflector is basically:

1. get all the methods for the class
2. remove any methods that don't match the name
3. remove any methods that don't match the parameter count
4. if there are any non-bridge methods left, remove all the bridge methods
5. if there is a single method left, that is the method
6. if there are many methods left, compare types and try and choose one

So bridge methods are thrown out before any argument type information is used to filter the list of possible methods. It seems like maybe the type filtering should happen first then the bridge method filtering. But I admit my grasp of bridge methods is pretty loose (and slipping?).
...