clojure gen-class 返回自己的 class
clojure gen-class returning own class
我现在正在用 Clojure 制作一个 class 对象,它有一个返回对象本身的方法。
用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
虽然我已经用谷歌搜索了这个问题,但找不到任何答案。有人知道这个问题的解决方案吗?
预先感谢您的帮助。
我不确定这是唯一的方法,但是,为了在方法签名中使用生成的 class 类型,您可以分两步生成 class - 虽然它仍然是在一个文件中并一次编译:
- 仅使用构造函数调用
gen-class
- 使用状态、全套构造函数和方法再次调用
gen-class
。
我尝试了各种方法,包括前向声明,但最终只有上述方法有效。我确实稍微扩展了你的例子。请注意 copy
方法不是很有用 原样 因为 Point
是不可变的,但您可能希望为 class 提供突变器。
完整列表:
(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)))))
为了生成 classes,配置 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
原因分析
问题是因为在 class myclass.Point
实际生成之前,编译器不知道 :methods
指令中的 myclass.Point
。虽然这不是 Java class 的问题,但 Clojure 编译器似乎不支持这个用例,(也许有充分的理由。)
解决方案
我觉得实现接口(在 :implements
指令中)比像您的示例那样定义自定义方法要容易得多。这也可能表明 "programming to interfaces." 从 Clojure 生成接口的良好实践是可行的,例如gen-interface
。只要确保 gen-interface
是在 gen-class
形式之前编译的。
或者,我更喜欢使用 Java 中的接口和 Clojure 中的实现来创建一个多语言项目。这是使用 leiningen 执行此操作的代码片段:
// 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中,虽然我修改了
适合您问题的代码片段。
我现在正在用 Clojure 制作一个 class 对象,它有一个返回对象本身的方法。
用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
虽然我已经用谷歌搜索了这个问题,但找不到任何答案。有人知道这个问题的解决方案吗?
预先感谢您的帮助。
我不确定这是唯一的方法,但是,为了在方法签名中使用生成的 class 类型,您可以分两步生成 class - 虽然它仍然是在一个文件中并一次编译:
- 仅使用构造函数调用
gen-class
- 使用状态、全套构造函数和方法再次调用
gen-class
。
我尝试了各种方法,包括前向声明,但最终只有上述方法有效。我确实稍微扩展了你的例子。请注意 copy
方法不是很有用 原样 因为 Point
是不可变的,但您可能希望为 class 提供突变器。
完整列表:
(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)))))
为了生成 classes,配置 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
原因分析
问题是因为在 class myclass.Point
实际生成之前,编译器不知道 :methods
指令中的 myclass.Point
。虽然这不是 Java class 的问题,但 Clojure 编译器似乎不支持这个用例,(也许有充分的理由。)
解决方案
我觉得实现接口(在 :implements
指令中)比像您的示例那样定义自定义方法要容易得多。这也可能表明 "programming to interfaces." 从 Clojure 生成接口的良好实践是可行的,例如gen-interface
。只要确保 gen-interface
是在 gen-class
形式之前编译的。
或者,我更喜欢使用 Java 中的接口和 Clojure 中的实现来创建一个多语言项目。这是使用 leiningen 执行此操作的代码片段:
// 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中,虽然我修改了 适合您问题的代码片段。