【问题标题】:How to correctly import user defined classes in Clojure如何在 Clojure 中正确导入用户定义的类
【发布时间】:2013-06-12 07:43:37
【问题描述】:

我正在使用 Leiningen 和 Clojure,对于我的一生,我无法理解为什么 Clojure 使正确导入命名空间变得如此困难。这是以下错误

这是我在core.clj 文件中的内容:

; namespace macro
(ns animals.core
  (:require animals.animal)
  (:use animals.animal)
  (:import (animals.animal Dog))
  (:import (animals.animal Human))
  (:import (animals.animal Arthropod))
  (:import (animals.animal Insect)))

; make-animals will create a vector of animal objects
(defn make-animals []
  (conj []
        (Dog. "Terrier" "Canis lupis familiaris")
        (Human. "Human" "Homo sapiens")
        (Arthropod. "Brown Recluse" "Loxosceles reclusa")
        (Insect. "Fire Ant" "Solenopsis conjurata")))

; print-animals will print all the animal objects
(defn print-animals [animals]
  (doseq [animal animals]
    (println animal)))

; move-animals will call the move action on each animal
(defn move-animals [animals]
  (doseq [animal animals]
    (animals.animal/move animal)))

; entry to main program
(defn -main [& args]
  (let [animals make-animals]
    (do
      (println "Welcome to Animals!")
      (println "-------------------")
      (print-animals animals))))

然后,在 REPL 中,我输入以下内容(在lein 项目的 src/ 目录中):

user> (require 'animals.core)
nil
user> (animals.core/-main)
ClassNotFoundException animals.core  java.net.URLClassLoader$1.run (URLClassLoader.java:202)

好吧……什么?为什么?

作为参考,这是我的文件animal.clj,也在animals 目录中:

(ns animals.animal)

(defprotocol Animal
  "A simple protocol for animal behaviors."
  (move [this] "Method to move."))

(defrecord Dog [name species]
  Animal
  (move [this] (str "The " (:name this) " walks on all fours.")))

(defrecord Human [name species]
  Animal
  (move [this] (str "The " (:name this) " walks on two legs.")))

(defrecord Arthropod [name species]
  Animal
  (move [this] (str "The " (:name this) " walks on eight legs.")))

(defrecord Insect [name species]
  Animal
  (move [this] (str "The " (:name this) " walks on six legs.")))

【问题讨论】:

    标签: java clojure leiningen nrepl


    【解决方案1】:

    将您的代码粘贴到一个新的 Leiningen 项目中,由于-main 中的拼写错误,我得到了一个不同的错误:(let [animals make-animals] ...) 应该是(let [animals (make-animals)] ...)。通过此更改,一切正常:

    user=> (require 'animals.core)
    nil
    user=> (animals.core/-main)
    Welcome to Animals!
    -------------------
    #animals.animal.Dog{:name Terrier, :species Canis lupis familiaris}
    #animals.animal.Human{:name Human, :species Homo sapiens}
    #animals.animal.Arthropod{:name Brown Recluse, :species Loxosceles reclusa}
    #animals.animal.Insect{:name Fire Ant, :species Solenopsis conjurata}
    nil
    

    顺便说一下,只要在项目目录中的某个位置调用 lein repl,具体从哪里调用都没有关系。

    我敢猜测,当您第一次尝试 require 时,您的命名空间有问题,现在由于 REPL 中的某些命名空间加载状态,它无法加载。您可能想尝试(require :reload 'animals.core),如果这不起作用,请重新启动您的 REPL。 (如果你再次遇到它,你也可以将整个 REPL 交互粘贴到 ClassNotFoundException 某处。)

    另外,关于您的ns 表单:

    1. 你不应该同时 :require:use 同一个命名空间; :use 已经:requires 了。

    2. 更常见的是使用单个:import 子句(实际上,每个子句类型一个子句);例如,

      (:import (animals.animal Dog Human Arthropod Insect))
      

      这在 Clojure 中纯粹是一种风格问题,但在 ClojureScript 中它实际上是语言所要求的。

    【讨论】:

    • 另一方面,如果您已经需要命名空间,您甚至不需要导入类。每条记录都有为其定义的 2 个附加功能,即带有前缀的记录名称。 ->Record 的行为类似于构造函数,但它是一个函数。 map->Record 采用属性映射。例如,这可以让您使用(animals.core/->Dog "Terrier" "Canis lupis familiaris"),并且可能会使您的代码在 Clojure 实现中更具可移植性。
    猜你喜欢
    • 2021-08-30
    • 2022-08-16
    • 2020-08-02
    • 1970-01-01
    • 2017-03-23
    • 1970-01-01
    • 2019-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多