2024年Clojure状态调查中分享您的想法!

欢迎!请查阅关于页面以了解更多关于此信息的工作方式。

+18
Java互操作

问题

Clojure调用Java的可变参数方法需要为最后一个参数创建一个对象数组。这在进行互操作时是一个常见的困惑之源。

例如,尝试调用java.util.Collections.addAll(Collection c, T... elements)

user=> (Collections/addAll [] (object-array 0)) false user=> (Collections/addAll []) IllegalArgumentException No matching method: addAll clojure.lang.Compiler$StaticMethodExpr.<init> (Compiler.java:1401)

Method类提供了isVarArg()方法,可以用来告知编译器以不同的方式处理。

有关更多信息,请参阅http://groups.google.com/group/clojure/browse_thread/thread/7d0d6cb32656a621

最新补丁已删除,因为不完整,目标不明确

Java中的可变参数

如目前所示,此问题的范围仅限于省略可变参数,但这只是Clojure处理可变参数与Java不同的一个案例。为了完整性,以下简要概述了Java如何处理可变参数方法,这些信息可能有助于进行讨论,以了解Clojure如何以不同的方式处理以及此问题的目标是什麼。

假设以下设置

`
public class VarArgs {

public static class SingleVarargMethod {
    public static void m(String arg1, String... args) {}
}

public static class MultipleVarargMethods {
    public static void m(String... args) {}
    public static void m(String arg1) {}
    public static void m(String arg1, String... args) {}
}

}
`

|Java|可能的Clojure等价面? |评论|
| :-- | :-- | :-- | :-- |
|{{VarArgs.SingleVarargMethod.m("a");}} | {{(SingleVarargMethod/m "a")}} | |
|{{VarArgs.SingleVarargMethod.m("a", "b");}} | {{(SingleVarargMethod/m "a" "b")}} | |
|{{VarArgs.SingleVarargMethod.m("a", "b", "c");}} | {{(SingleVarargMethod/m "a" "b" "c")}} | |
|{{VarArgs.SingleVarargMethod.m("a", new String[]{"b", "c"});}} | {{(SingleVarargMethod/m "a" (object-array ["b" "c"]))}} | |
|{{VarArgs.MultipleVarargMethods.m();}} | {{(MultipleVarargMethods/m)}} | |
|{{VarArgs.MultipleVarargMethods.m((String) null);}} | {{(MultipleVarargMethods/m nil)}} |使用类型提示以区分?|
|{{VarArgs.MultipleVarargMethods.m((String[]) null);}} | {{(MultipleVarargMethods/m nil)}} |使用类型提示以区分? |
|{{VarArgs.MultipleVarargMethods.m("a", null);}} | {{(MultipleVarargMethods/m "a" null)}} | |
|{{VarArgs.MultipleVarargMethods.m("a", new String[]{});}} | {{(MultipleVarargMethods/m "a" (object-array 0))}} | |
|{{VarArgs.MultipleVarargMethods.m(new String[]{"a"});}} | {{(MultipleVarargMethods/m (object-array ["a"]))}} | |
|{{VarArgs.MultipleVarargMethods.m("a", new String[]{"b", "c"});}} | {{(MultipleVarargMethods/m "a" (object-array ["b" "c"]))}} | |

13 个回答

0

由导入工具发表的评论

http://www.assembla.com/spaces/clojure/tickets/440 转换而来

0

由 ataggart 发表的评论

补丁增加了对 varargs 的支持。在 CLJ-445 补丁的基础上进行了扩展。

0

由 ataggart 发表的评论

补丁已更新至当前的 CLJ-445 补丁。

0

由 klauern 发表的评论

这个工单在挂起吗?我发现我经常输入 {{(.someCall arg1 arg2 (into-array SomeType nil))}} 来确保调用正确的方法。这个工单听起来像是它可以解决我经常使用的那个多余的 {{into-array}} 参数。

0

由 jafingerhut 发表的评论

2012 年 10 月 29 日上传的 fixbug445.diff 是由 Alexander Taggart 于 2010 年 10 月 23 日编写的。我只是从旧的 Assembla 工作票跟踪系统中将其复制到这里,以便更容易访问。不出所料,它不会干净地应用到最新的 mastery。我不知道更新它需要多少工作量,但根据 'patch',只有几块版本不能干净应用。请参阅此处 JIRA 工作流程页面上的“更新陈旧的补丁”部分:http://dev.clojure.org/display/design/JIRA workflow

0

由 jafingerhut 发表的评论

唉。删除了附件,因为这可能是为CLJ-445准备的,或者至少它的名字是这样的。CLJ-445肯定有很大的注释历史,所以如果其补丁之一或多个解决了这个问题,那么你可以阅读那里的讨论以查看历史。

我不了解任何关于工单“挂起”的状态,除了Rich Hickey在一两条评论中明确表示他想等一段时间才进行更改的情况。只是一些贡献者选择要工作的工单和审核者选择要审核的工单。

0

由:alexmiller发表的评论

我希望看到这个工单的最新补丁,该补丁专门针对varargs问题,而不是建立在其他提到的工单和补丁(优先级较低)之上。

0

由:ragge发表的评论

我尝试了这个,附上一个初步的补丁,其中一些部分我不太确定/满意,因此欢迎反馈。

该补丁采取以下方法

  1. 教{{Reflector/getMethods}}如何找到匹配的变长参数方法。除了当前约束条件外,如果方法也是一个变长参数方法,并且方法的多项式度比请求的多项式度多一个,它也可以匹配。这意味着这是一个我们可以调用的变长参数方法,但用户没有提供变长参数。
  2. 在{{MethodExpr/emitTypedArgs}}中,我们处理被调用方法中的参数比提供的参数多一个的情况。唯一应该发生这种情况的情况是它是一个变长参数方法,且最后一个参数没有被提供。在这种情况下,我们在堆栈上推进一个新的空对象数组。

我对第二部分的实现不太确定。这可能会在未来打开一些难以理解的错误。一个选项是进行更谨慎的安全措施,并确保例如这确实是最后一个参数,或者甚至传递{{Method}}对象(或一个变长参数标志),这样我们就知道我们可以期待什么,我们需要做什么。

0

由:ragge发表的评论

我意识到我的补丁遗漏了两个重要的案例;在Reflector中的接口处理和多重匹配方法的处理。我也会研究这个问题,但仍然欢迎对{{MethodExpr/emitTypedArgs}}中的方法进行反馈。

0

由:alexmiller发表的评论

我赞同使用isVarArg()来明确处理这种情况,而不是猜测是否处于这种情况。我们应该检查调用具有过多或过少参数的变元方法的行为(并在需要的地方添加测试),并且也要确保非变元方法的行为没有发生变化。

值得注意的是,作为一个一般规则,现有的AOT编译过的代码可能依赖于调用公开的Reflector方法,因此如果你更改了公开Reflector方法的签名,你应该保留一个具有旧算术字符的版本,以便向后兼容。

0

评论者:gshayban

编译器实现的任何额外逻辑都需要区分

`

public static class MultipleVarargMethods {
    public static void m(String... args) {}
    public static void m(String arg1, String... args) {}
}

`

我认为这在没有破坏代码的情况下难以通用地实现。

与其省略变元,不如不通过繁琐的数组构造来处理它们。另一种选择是引入新的显式糖,必须选择加入

(Whatever/varargs a b c ... x y z)

其中...或其他类似的内容将被编译器中的StaticMethodExpr或InstanceMethodExpr理解,并可以类型提示以解决歧义。这不会是一个破坏性更改。

0

评论者:pbwolf

如果javac可以区分这些方法,可以不通过调用者提供"..."分区,Clojure也应该如此。(但在特定的"MultipleVarargMethods"类中,javac无法区分)

`
public class MultipleVarargMethods {

public static void m(String... args) {}
public static void m(String arg1, String... args) {}
public static void main(String [] args) {
    m("bankruptcy");
    m("bankruptcy","in","progress");
}

}

MultipleVarargMethods.java:5: 错误:对m的引用不明确

    m("bankruptcy");
    ^

MultipleVarargMethods中的m(String...)方法和方法m(StringMultipleVarargMethods.java:6: 错误:对m的引用不明确

    m("bankruptcy","in","progress");
    ^

MultipleVarargMethods中的m(String...)方法和方法m(String2个错误
`

)

0
参考资料:https://clojure.atlassian.net/browse/CLJ-440 (由ataggart报告)
...