gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 20:53:59 24 4
gpt4 key购买 nike

我正在尝试使用 ojAlgo来自 Clojure 的 Java 库,但我无法调用 weight Expression的方法类。

为了演示它,我有一个具有以下依赖项的 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!

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

  1. Unhandled java.lang.IllegalArgumentException No matching method weight found taking 1 args for class
    org.ojalgo.optimisation.Expression

问题:为什么会出现此错误?如何调用 weight 方法而不出现错误?

但有趣的是我可以编写一个小的Java类来调用这个方法:

import org.ojalgo.optimisation.Expression;

public class CallExpressionWeight {
public static void apply(Expression e, double w) {
e.weight(w);
}
}

这有效:

(CallExpressionWeight/apply e 1.0)
;; => nil

此外,我使用了 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

这个weight方法有些可疑......

最佳答案

这对我来说也很奇怪。我认为您可能有一个与 javadoc 不匹配的库版本,或者您可能正在针对与 clojure 代码不同的版本编译 java shim,但这些都不是这种情况。我创建了一个存储库,以便轻松重现您的问题:https://github.com/amalloy/ojalgo

但是当我尝试你的代码时(任何人都可以通过该代码在该存储库中执行

lein run -m clojure.main -- -e \
'(-> (org.ojalgo.optimisation.ExpressionsBasedModel.) (.addExpression) (.weight 0))'

),我没有得到与您完全相同的错误。相反,我看到

Caused by: java.lang.IllegalArgumentException: Can't call public method of non-public class: public final org.ojalgo.optimisation.ModelEntity org.ojalgo.optimisation.ModelEntity.weight(java.lang.Number)

这对我来说有一定道理。你得到的Expression实际上是隐藏类ModelEntity的子类,而这个类才是真正定义权重的类。 clojure 编译器不知道互操作调用中涉及的值的静态类型,当它尝试查找名为 weight 的方法时,它会猜测类中实际定义 weight 的方法...但是这个类不是公共(public)的,因此不允许反射地调用它的方法。

通常,有一个简单的解决方法:使用您要使用的静态类型对变量进行类型提示:

(.weight ^Expression e 0)

但在这种情况下,即使这样也不起作用。这与 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

)看起来像这样,缩写为:

abstract class ModelEntity<ME extends ModelEntity<ME>> {
public final ME weight(Number) {...}
}

public final class Expression extends ModelEntity<Expression> {
}

Expression 类确实继承了一个名为 weight 的成员,但由于他们对泛型所做的不寻常的事情,javac 实际上生成了一个合成的 bridge method使用一个特殊的名称来委托(delegate)真实的 weight 方法。因此,当 clojure 编译器查找名为 weight 的内容时,它找到的唯一一个就是该非公共(public)类中不允许对其进行操作的那个。

一方面,我认为将其称为 clojure 中的错误是公平的,但另一方面,它似乎不太可能被修复:它在很大程度上取决于 javac 的实现细节,而 javac 的实现细节可能随时发生变化。我认为您无法做任何事情来说服 clojure 调用此方法,因此我认为您编写的 java shim 是最好的解决方法。

关于clojure - 无法从 Clojure 调用特定的 java 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58968216/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com