2016-05-03 7 views
2

Я пытаюсь использовать библиотеку DNSJava из clojure. Я стараюсь:Доступ к общедоступному методу, определенному в абстрактном базовом классе от Clojure

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 быстро 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 Я вижу, что он расширяет TXTBase, который является абстрактным классом, где реализован getStrings. Поскольку TXTRecord расширяет его, я должен иметь доступ к getStrings через которые (и я зашел так далеко, чтобы написать программу Java, чтобы проверить его.

Кто-нибудь знает, как я могу получить доступ к этому через Clojure?

EDIT Рабочая программа 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)); 
      } 
     } 

    } 
} 
+0

Ну, 'TXTBase' не объявляется как' public'. Поэтому я не знаю, почему у вас будет доступ к любым методам, объявленным в этом классе вне пакета 'org.xbill.dns'. И это именно то, о чем намекает сообщение об исключении - «Нельзя вызвать публичный метод для ** непубличного ** класса» (выделено мной). Не могли бы вы добавить программу Java, которая работает, чтобы мы могли сравнить с версией Clojure, пожалуйста? –

+0

Добавлено. Оба используют dnsjava 2.1.7 off maven. Я могу предоставить файлы gradle и project.clj, если это необходимо. –

ответ

3

похоже, что это известная ошибка CLJ-1243 Вы можете найти более подробную информацию и, вероятно, первопричину чтением Netty issue description:.

Публичные методы, унаследованные от AbstractBootstrap, не могут быть вызваны через API отражения Java из-за давних ошибок, таких как JDK-4283544.

Это серьезная проблема для альтернативных языков JVM, которые используют отражение для поиска методов Java. Например, Clojure использует отражение в своем компиляторе и не может вызывать эти методы вообще, , как сообщается в CLJ-1243.

И часть описания из JDK bug:

java.lang.reflect.Field (получить * и установить *) и метод (вызов) основывает свою проверку доступа на объявляющем классе. Это противоречит JLS, , который определяет доступность с точки зрения ссылочного типа.

1

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>