访问未在 Clojure 中导入的 Java 类 的字段和方法

Accessing fields, methods of Java classes that aren't imported in Clojure

我开始更深入地了解 Clojure-Java 互操作。如果我在 Clojure 中创建一个 Java class,我需要导入它,但如果我只是使用 class 的字段或方法,我不必导入它。例如:

(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 实例。

但是,我访问 Students 实例的 yard 字段和 setObjectLocation() 方法,以及 widthheight 字段的事实Continuous2D 实例不会导致任何问题,即使我没有导入 StudentsContinuous2D classes.

这是否意味着 Clojure 在运行时使用反射来访问 Java 字段和方法?那是低效的吗?如果我需要一个函数的速度,为 Java classes 添加类型声明,以及缺少的导入语句是否有利?这会阻止反射吗?

[编辑:正如我对 Arthur Ulfeldt 的回答的第二条评论所暗示的那样,我现在认为,由于在编译时不知道 classes 而导致的任何低效率可能与来自以下原因的低效率没有太大区别函数包含在变量中这一事实。如果我担心 that,我会忘记 Clojure 和纯 Java 编程(maybe),而不是尝试使用来自 Clojure 的 Java 库。对我来说,一个我可以使用 Clojure 而不是 Java 的世界是一个更好的世界。]

import 只是为了使它如此you don't have to type the full name of the class,包括它的包名,每次你想用它。 如果 class 在 class 路径上,您可以从任何地方使用它的全名来调用它。如果您作为导入语句,您可以仅通过 class 名称调用它,但只能从存在该导入的命名空间内调用它。

我将从一个新项目开始,并添加一个显然在默认项目中的任何地方使用的依赖项,在本例中是用于监视内容的 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"]])

那么首先,我会看一下 class 的全名,没有任何导入

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)

然后再看看class:

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

我们可以看到导入是按命名空间进行的,尽管 class路径上的 classes 随处可用。