请在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" 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

由:导入器发表的评论

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工单跟踪系统复制到这里,以便更容易访问。毫不奇怪,它不能很好地应用到最新的master。我不知道更新它需要多少努力,但根据'patch',只有几块不能很好地应用。请参阅此处的“更新过时的补丁”部分:http://dev.clojure.org/display/design/JIRA workflow

0

由:jafingerhut发表的评论

唉。删除了附件,因为它是为CLJ-445,至少命名为这样。CLJ-445肯定有长长的评论历史,所以如果一个或多个补丁解决了这个问题,您可以在那里阅读讨论以查看历史。

我不知道关于工单的“挂起”状态,除了富里奇·希克利在评论中明确表示他要等一段时间再做出更改的少数几个。只是有些工单是贡献者选择要工作的,而有些是筛子选择要筛选的。

0

评论者:alexmiller

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

0

评论者:ragge

我对此进行了一些尝试,附上了初始补丁,其中一部分我觉得不是很确定/满意,所以欢迎反馈。

补丁采取了以下做法

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

我对我的第二个部分实现不太确定。这可能会在将来打开一些难以理解的错误。一个选择是更具防御性,并确保实际上是最后一个参数,例如,或者传递{{Method}}对象(或varargs标志)以便我们知道我们可以期待和需要做什么。

0

评论者:ragge

我意识到我的补丁遗漏了两个重要情况;在Reflector中的接口处理以及处理多个匹配方法。我将也查看这个,但仍然感谢对{{MethodExpr/emitTypedArgs}}中方法的反馈。

0

评论者:alexmiller

我倾向于使用isVarArg()来显式处理这种情况,而不是猜测我们是否处于这种情况。我们应该检查调用带有过多或少于正确参数的var args方法的行为(并在需要的地方添加测试),并且也要确保非varargs的情况没有出现行为变化。

请注意,作为一般规则,现有的AOT编译过的代码可能会依赖于调用公开的Reflector方法,所以如果您更改了公开Reflector方法的签名,您应该为保持向后兼容性,保留一个带有旧参数数且具有某些默认行为的旧版本。

0

评论者:gshayban

编译器实现的所有额外逻辑都需要区分以下内容

`

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

`

我认为在没有提供"..."分区的情况下可能无法泛型地做到这一点,从而破坏代码。

与其省略varargs,不如在不进行麻烦的数组构造的情况下处理它们。一种替代方案是引入新的显式糖,您必须启用它

(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...)和在MultipleVarargMethods中的方法m(String,String...)匹配
MultipleVarargMethods.java:6: 错误:对m的引用不明确

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

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

)

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