Доступ к полям, методам классов Java, которые не импортированы в Clojure

Я начинаю больше разбираться в взаимодействии Clojure-Java. Если я создаю класс Java в Clojure, мне нужно его импортировать, но если я просто использую поля или методы класса, мне не нужно его импортировать. Например:

(ns students.Student
  (:import [sim.util Double2D])

(defn -step
  [this students]  ; students is a students.Students, extends sim.engine.SimState
  (let [yard (.-yard students) ; a sim.field.continuous.Continuous2D
        yard-width (.-width yard)
        yard-height (.-height yard)]
    (.setObjectLocation yard this (Double2D. (/ yard-height 2) (/ yard-width 2)))))

Это не скомпилируется без импорта Double2D, потому что код создает экземпляр Double2D.

Однако тот факт, что я обращаюсь к полю yard и методу setObjectLocation() экземпляра Students, а также к полям width и height экземпляра Continuous2D, не вызывает проблем, хотя я не импортирую классы Students или Continuous2D.

Означает ли это, что Clojure использует отражение во время выполнения для доступа к полям и методам Java? Это неэффективно? Если мне нужна скорость в функции, может быть выгодно добавить объявления типов для классов Java вместе с отсутствующими операторами импорта? Помешает ли это рефлексии?

[РЕДАКТИРОВАТЬ: Как следует из моего второго комментария к ответу Артура Ульфельдта, теперь я думаю, что любая неэффективность, возникающая из-за незнания классов во время компиляции, вероятно, не сильно отличается от неэффективности, связанной с тем, что функции содержатся в переменных. Если бы меня беспокоил это, я бы забыл о Clojure и программировал на чистой Java (возможно), вместо того, чтобы пытаться использовать Java-библиотеки из Clojure. Для меня мир, в котором я могу использовать Clojure вместо Java, лучше.]


person Mars    schedule 24.04.2015    source источник


Ответы (1)


Импорт используется только для того, чтобы не нужно было вводить полное имя класса, включая имя пакета, каждый раз, когда вы хотите его использовать. если класс находится в пути к классам, вы можете вызывать его по полному имени из любого места. Если вы используете оператор импорта, вы можете вызвать его только по имени класса, но только из пространства имен, где этот импорт существует.

Я начну с нового проекта и добавлю зависимость, которая явно не используется где-либо в проекте по умолчанию, в данном случае это клиент Riemann для мониторинга (это отличная программа, проверьте ее)

lein new hello

и добавьте отл.

(defproject hello "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :aot :all
  :main hello.core
  :profiles {:uberjar {:aot :all}}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [com.aphyr/riemann-java-client "0.3.1"]])

тогда первым делом я посмотрю на этот класс по его полному имени без импорта

hello.core> com.aphyr.riemann.client.RiemannClient
com.aphyr.riemann.client.RiemannClient

затем попробуйте короткое имя:

hello.core> RiemannClient
CompilerException java.lang.RuntimeException: Unable to resolve symbol: RiemannClient in this context, compiling:(/tmp/form-init3055907211270530213.clj:1:5933) 

что не работает:

hello.core> (import '[com.aphyr.riemann.client RiemannClient])
com.aphyr.riemann.client.RiemannClient

Затем, если мы добавим импорт и повторим попытку:

hello.core> RiemannClient
com.aphyr.riemann.client.RiemannClient

имя разрешается из пространства имен пользователя.
Если я изменяю пространства имен:

hello.core> (in-ns 'foo)

а затем снова посмотрите на класс:

foo> RiemannClient
CompilerException java.lang.RuntimeException: Unable to resolve symbol: RiemannClient in this context, compiling:(/tmp/form-init3055907211270530213.clj:1:5933) 
foo> com.aphyr.riemann.client.RiemannClient
com.aphyr.riemann.client.RiemannClient

мы видим, что импорт выполняется для каждого пространства имен, хотя классы в пути к классам доступны везде.

person Arthur Ulfeldt    schedule 24.04.2015
comment
Спасибо - это очень полезно. Я не понимал всего этого в классах Java. Мне все еще интересно получить доступ к методам и полям необъявленного класса Java. Проверка типа встроенной функции Clojure означает работу с одним из известного диапазона возможных типов, и насколько я понимаю, в некоторых случаях компилятор может определить типы заранее. Но при применении .-x или .setObjectLocation к объекту yard, например, компилятор даже не знает, что это за функции, до момента выполнения. Это кажется более сложным, чем работа с динамической типизацией, включающей встроенные функции. - person Mars; 25.04.2015
comment
О, подожди. Я говорю о функциональном языке. [хлопает себя по голове] Clojure не обязательно знает, к какой функции относится любое имя, во время компиляции. [Я могу написать (def inc dec), ну и что? Сила и красота сопряжены с риском.] Так что в незнании значения .setObjectLocation нет ничего особенного. - person Mars; 25.04.2015
comment
Если вы сосредоточитесь на том факте, что единицей компиляции является s-выражение верхнего уровня, вам поможет такая медитация. Def обновляет корневые значения переменных в пространстве имен, которое не является постоянной структурой данных. Он действительно обновляется на месте, и значения функции известны во время компиляции, где время компиляции соответствует этому выражению. переопределение var не изменяет функции из-под уже скомпилированного кода. В основном это связано с тем, что единицей компиляции является одно выражение. Если вы хотите, чтобы функции изменились из-под вашего кода, вызовите vars явно (#'inc 4) - person Arthur Ulfeldt; 25.04.2015
comment
Ах, верно. Я понимаю. Спасибо. Но компилятор по-прежнему не знает, какая функция находится в лексической переменной или, например, какую функцию выдаст функция более высокого порядка. Тот факт, что он не знает, какие элементы содержатся в экземпляре Java, которые будут значением лексической переменной, кажется похожим. - person Mars; 25.04.2015