无法从 Clojure 调用特定的 java 方法

Rul*_*lle 4 clojure

我正在尝试使用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)

但是,最后一行失败并出现错误

  1. 未处理的 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方法有点可疑......

ama*_*loy 6

这对我来说也很奇怪。我认为您可能有一个与 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 是最好的解决方法。