【问题标题】:Access public method defined on abstract base class from Clojure从 Clojure 访问在抽象基类上定义的公共方法
【发布时间】:2016-05-03 03:14:18
【问题描述】:

我正在尝试使用 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)

Ok quick 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)

嗯。在我进一步搜索谷歌,并阅读interop 上的完整页面,但我没有任何运气。

查看TXTRecord 的源代码,我发现它扩展了TXTBase,这是一个实现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));
            }
        }

    }
}

【问题讨论】:

  • 好吧,TXTBase 没有声明为public。所以我不知道为什么你可以访问在org.xbill.dns 包之外的那个类上声明的任何方法。这正是异常消息所暗示的内容——“不能在 non-public 类上调用公共方法”(添加了重点)。请您添加有效的 Java 程序,以便我们与 Clojure 版本进行比较,好吗?
  • 已添加。两者都使用 dnsjava 2.1.7 off maven。如果需要,我可以提供 gradle 和 project.clj 文件。

标签: clojure clojure-java-interop


【解决方案1】:

看起来这是一个已知的错误CLJ-1243。你可以通过阅读Netty issue description找到更多细节和可能的根本原因:

继承自 AbstractBootstrap 的公共方法不能被调用 由于长期存在的错误,例如,通过 Java 反射 API JDK-4283544。

对于使用其他 JVM 语言来说,这是一个严重的问题 反射以发现 Java 方法。例如,Clojure 使用 在它的编译器中反射,它根本不能调用这些方法, 如 CLJ-1243 中所述。

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

java.lang.reflect.Field (get* and set*) 和 Method (invoke) 基于它们的 声明类的访问检查。这与JLS相反, 它根据引用类型定义可访问性。

【讨论】:

    【解决方案2】:

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

    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> 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-21
      • 1970-01-01
      • 2014-10-14
      • 2015-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多