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

欢迎!请查看关于页面以获取更多关于这是如何工作的信息。

+18
Java互操作

问题

Clojure调用Java的可变参数方法需要为最后一个参数创建一个对象数组。在互操作时,这是一个常见的混淆来源。

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

用户=> (Collections/addAll [] (object-array 0)) false 用户=> (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 null)}} |使用类型提示消除歧义 |
|{{VarArgs.MultipleVarargMethods.m("a", null);}} | {{(MultipleVarargMethods/m "a" nil)}} | |
|{{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

由:importer发布的评论

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

0

由:ataggart发布的评论

修复补丁添加了对可变参数的支持。建立在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票据跟踪系统中复制到这里,以便更容易地访问。毫不奇怪,它无法完美地应用到最新的master版本中。我不知道更新它需要多少工作量,但根据'patch',只有几块无法完美贴合。请参阅此处的“更新过时的补丁”部分:[http://dev.clojure.org/display/design/JIRA+workflow](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(String,String...) 方法匹配
MultipleVarargMethods.java:6: 错误:对 m 的引用不明确

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

MultipleVarargMethods 类中的 m(String...) 方法和 m(String,String...) 方法匹配
2 个错误
`

)

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