clojure gen-class返回自己的班级

时间:2015-03-29 13:39:12

标签: clojure clojure-java-interop

我现在用Clojure创建一个类对象,它有一个返回对象本身的方法。

用Java编写,我想做的对象是,

class Point {
    public double x;
    public double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public Point copy() {
        return new Point(this.x, this.y);
    }
}

我写的当前clojure代码就像,

(ns myclass.Point
  :gen-class
  :prefix "point-"
  :init init
  :state state
  :constructors {[double double] []}
  :methods [[copy [] myclass.Point]]))

(defn point-init [x y]
   [[] {:x x :y y}])

(defn point-copy [this]
   this)

但是,我收到如下错误。

java.lang.ClassNotFoundException: myclass.Point

虽然我搜索了这个问题,但我找不到任何答案。有谁知道这个问题的解决方案?

提前感谢您的帮助。

2 个答案:

答案 0 :(得分:3)

我不确定这是唯一的方法,但是,为了在方法签名中使用生成的类类型,您可以分两步生成类 - 尽管它仍然在一个文件中并在一次传递中编译:< / p>

  • 仅使用构造函数
  • 调用gen-class
  • 再次使用状态,完整的构造函数和方法调用gen-class

我尝试了各种方法,包括前向声明,但只有上述内容最终有效。我确实扩展了你的例子。请注意,copy方法不是非常有用 as-is ,因为Point是不可变的,但您可能希望为您的类提供更改器。

完整列表:

(ns points.Point)

;; generate a simple class with the constructors used in the copy method
(gen-class
 :name points.Point
 :init init
 :constructors {[] []
                [double double] []})

;; generate the full class 
(gen-class
 :name points.Point
 :prefix pt-
 :main true
 :state coordinates
 :init init
 :constructors {[] []
                [double double] []}
 :methods [[distance [points.Point] double]
           [copy [] points.Point]])

(defn pt-init
  ([] (pt-init 0 0))
  ([x y]
   [[] {:x x :y y}]))

(defn pt-copy 
  "Return a copy of this point"
  [this]
  (points.Point. (:x (.coordinates this)) (:y (.coordinates this))))

(defn pt-distance [^points.Point this ^points.Point p]
  (let [dx (- (:x (.coordinates this)) (:x (.coordinates p)))
        dy (- (:y (.coordinates this)) (:y (.coordinates p)))]
    (Math/sqrt (+ (* dx dx) (* dy dy)))))

(defn pt-toString [this]
  (str "Point: " (.coordinates this)))

;; Testing Java constructors and method call on Point class
(import (points Point))
(defn pt-main []
  (let [o (Point.)
        p (points.Point. 3 4)]
    (println (.toString o))
    (println (.toString p))
    (println (.distance o p))
    (println (.distance p (.copy p)))))

要生成类,请使用

行配置project.clj
:aot [points.Point]

使用lein进行测试得出:

tgo$ lein clean
tgo$ lein compile
Compiling points.Point
tgo$ lein run
Point: {:x 0, :y 0}
Point: {:x 3.0, :y 4.0}
5.0
0.0

答案 1 :(得分:0)

原因分析

问题是因为在实际生成类myclass.Point之前,编译器不了解:methods指令中的myclass.Point。尽管这对Java类来说不是问题,但是Clojure编译器似乎不支持此用例,(也许出于充分的理由。)

解决方案

我觉得实现接口(在:implements指令中比定义像您的示例那样的自定义方法要容易得多。这也可能表明“对接口编程”的良好实践。从Clojure生成接口是可行的,例如gen-interface。只需确保在gen-interface表单之前先gen-class进行编译。

或者,我更喜欢用Java中的接口和Clojure中的实现创建一个多语言项目。这是使用莱宁根实现的代码片段:

// src/java/points/Point.java
package points;

public interface Point {
    public double distant(Point p);
    public Point copy();
}
;; project.clj
(defproject
  ;; Change these. The rests are the same.
  :aot [points.java-class]
  :source-paths ["src/clojure"]
  :java-source-paths ["src/java"])

;; src/clojure/points/java_class.clj
(ns points.java-class
  (:gen-class
   :name points.Point2D
   :implements [points.Point]  ;; no more ClassNotFoundException
   :prefix "point-"
   :init init
   :state state
   :constructors {[double double] []})

;; rests are the same

之所以有效,是因为Leiningen默认情况下会先编译Java源代码。

article也涵盖了此答案,尽管我修改了 代码段以适合您的问题。