我正在尝试使用Clojure 中的ojAlgo Java 库,但我无法调用Expression类的weight方法。
为了演示它,我有一个具有此依赖项的 Leiningen 项目: [org.ojalgo/ojalgo "47.3.1"]
我正在尝试这样做:
(ns ojalgo-test
(:require [clojure.reflect :as r])
(:import [org.ojalgo.optimisation ExpressionsBasedModel
Expression]
CallExpressionWeight))
(def m (ExpressionsBasedModel.))
;; => #'ojalgo-test/m
(def e (.addExpression m))
;; => #'ojalgo-test/e
(.weight e 1.0) ;; ERROR!
Run Code Online (Sandbox Code Playgroud)
但是,最后一行失败并出现错误
- 未处理的 java.lang.IllegalArgumentException 没有找到匹配的方法权重,为类
org.ojalgo.optimisation.Expression取 1 个参数
问题:为什么会出现此错误,以及如何调用该weight方法而不会出现错误?
但有趣的是,我可以写一个小的Java类来调用这个方法:
import org.ojalgo.optimisation.Expression;
public class CallExpressionWeight {
public static void apply(Expression e, double w) {
e.weight(w);
}
}
Run Code Online (Sandbox Code Playgroud)
这有效:
(CallExpressionWeight/apply e 1.0)
;; => nil
Run Code Online (Sandbox Code Playgroud)
此外,我使用该clojure.reflect/reflect函数来查看我的Expression实例的方法:
(def member-set (set (map :name (:members (r/reflect e)))))
;; => #'ojalgo-test/member-set
(contains? member-set 'setInfeasible)
;; => true
(contains? member-set 'weight)
;; => false
Run Code Online (Sandbox Code Playgroud)
这种weight方法有点可疑......
这对我来说也很奇怪。我认为您可能有一个与 javadoc 不匹配的库版本,或者您可能正在针对与 clojure 代码不同的版本编译 java shim,但这些都不是这种情况。我创建了一个 repo 来轻松重现您的问题:https : //github.com/amalloy/ojalgo。
但是当我尝试你的代码时(任何人都可以通过
lein run -m clojure.main -- -e \
'(-> (org.ojalgo.optimisation.ExpressionsBasedModel.) (.addExpression) (.weight 0))'
Run Code Online (Sandbox Code Playgroud)
),我没有得到完全相同的错误。相反,我看到
引起:java.lang.IllegalArgumentException:无法调用非公共类的公共方法:public final org.ojalgo.optimisation.ModelEntity org.ojalgo.optimisation.ModelEntity.weight(java.lang.Number)
这对我来说有些道理。你得到的Expression其实是隐藏类ModelEntity的子类,而这个类才是真正定义权重的。clojure 编译器不知道互操作调用中涉及的值的静态类型,并且当它试图找到一个名为weight它的方法时,它会猜测实际定义的类中的那个weight......但这个类不是公共的,所以不允许反射性地调用它的方法。
通常,有一个简单的解决方法:使用您要使用的静态类型对变量进行类型提示:
(.weight ^Expression e 0)
Run Code Online (Sandbox Code Playgroud)
但在这种情况下,即使这样也行不通。这与clojure.reflect无法找到此方法具有相同的根本原因。说这种方法实际上并不存在是有道理的!此库中的继承层次结构(您可以使用它进行调查)
javap -cp ~/.m2/repository/org/ojalgo/ojalgo/47.3.1/ojalgo-47.3.1.jar -p org.ojalgo.optimisation.Expression org.ojalgo.optimisation.ModelEntity
Run Code Online (Sandbox Code Playgroud)
) 看起来像这样,删节了:
abstract class ModelEntity<ME extends ModelEntity<ME>> {
public final ME weight(Number) {...}
}
public final class Expression extends ModelEntity<Expression> {
}
Run Code Online (Sandbox Code Playgroud)
Expression 类确实继承了一个名为 的成员weight,但是由于他们对泛型所做的不寻常的事情,javac 实际上生成了一个具有特殊名称的合成桥方法,该名称委托给真实weight方法或从真实方法委托。因此,当 clojure 编译器查找 named 时weight,它找到的唯一一个是该非公共类中不允许对其进行操作的那个。
一方面,我认为将其称为 clojure 中的错误是公平的,但另一方面,它似乎不太可能被修复:它在很大程度上取决于 javac 的实现细节,它可能随时更改。我认为您无法说服 clojure 调用此方法,因此我认为您编写的 java shim 是最好的解决方法。