从 Clojure 访问在抽象基础 class 上定义的 public 方法

Access public method defined on abstract base class from Clojure

我正在尝试使用 clojure 中的 DNSJava 库。我尝试以下操作:

dmarced.dns> (def results (.run (Lookup. "google.com" Type/TXT)))
#'dmarced.dns/results
dmarced.dns> (def r (get results 0))
#'dmarced.dns/r
dmarced.dns> r
#object[org.xbill.DNS.TXTRecord 0x687a3556 "google.com.\t\t3599\tIN\tTXT\t\"v=spf1 include:_spf.google.com ~all\""]
dmarced.dns> (class r)
org.xbill.DNS.TXTRecord
dmarced.dns> (instance? TXTRecord r)
true

太棒了!我从 docs 知道我应该能够使用 .getStrings 来获取记录的内容。

dmarced.dns> (.getStrings r)
Reflection warning, *cider-repl dmarced*:150:13 - reference to field getStrings can't be resolved.
IllegalArgumentException Can't call public method of non-public class: public java.util.List org.xbill.DNS.TXTBase.getStrings()  clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:88)

好的 google 告诉我这可以通过类型提示解决:

dmarced.dns> (.getStrings ^TXTRecord r)
Reflection warning, *cider-repl dmarced*:153:13 - call to method getStrings on org.xbill.DNS.TXTRecord can't be resolved (argument types: ).
IllegalArgumentException Can't call public method of non-public class: public java.util.List org.xbill.DNS.TXTBase.getStrings()  clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:88)

嗯。我进一步搜索了 Google,并阅读了 interop 上的整页,但我没有任何运气。

查看 TXTRecord I see that it extends TXTBase which is an abstract class where getStrings 的源代码已实现。由于 TXTRecord 扩展了它,我应该可以通过它访问 getStrings(我什至编写了一个 Java 程序来验证它。

有人知道我如何通过 Clojure 访问它吗?

编辑 工作java 程序

import org.xbill.DNS.*;

class Main {
    public static void main(String [] args) throws TextParseException {
        Lookup l = new Lookup("google.com", Type.TXT);
        Record [] rs = l.run();
        for(int i = 0; i < rs.length; i++) {
            TXTRecord tr = (TXTRecord)rs[i];
            for(int j = 0; j < tr.getStrings().size(); j ++) {
                System.out.println(tr.getStrings().get(j));
            }
        }

    }
}

看起来这是一个已知的错误CLJ-1243. You can find more details and probably root cause by reading Netty issue description:

The public methods inherited from AbstractBootstrap cannot be invoked via the Java reflection API due to long-standing bugs such as JDK-4283544.

This is a serious problem for alternative JVM languages which use reflection to discover Java methods. For example, Clojure uses reflection in its compiler, and it cannot invoke these methods at all, as reported in CLJ-1243.

以及来自 JDK bug 的部分描述:

java.lang.reflect.Field (get* and set*) and Method (invoke) base their access check on the declaring class. This is contrary to the JLS, which defines accessibility in terms of the reference type.

Piotrek 似乎对它发生的原因是正确的。 Clojure 错误报告建议编写 Java class 来获取信息。它不是很漂亮,但我设法解决了它

dmarced.core> (def txt-strings-method (doto (.getDeclaredMethod TXTBase "getStrings" nil) (.setAccessible true)))
#'dmarced.core/txt-strings-method
dmarced.core> (defn get-txt-strings [r]
                (.invoke m r nil))
#'dmarced.core/get-txt-strings
dmarced.core> (get-txt-strings r)
["v=spf1 include:_spf.google.com ~all"]
dmarced.core>