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 null)}} |使用类型提示来消除歧义?|
|{{VarArgs.MultipleVarargMethods.m((String[]) null);}} | {{(MultipleVarargMethods/m null)}} |使用类型提示来消除歧义? |
|{{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
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

0

评论者:jafingerhut

哎呀。删除了附件,因为它似乎是针对 CLJ-445 的,或者至少是这么命名的。CLJ-445 确实有很长的一段评论历史,所以如果其任何一个补丁解决了这个问题,那么你可以通过阅读那里的讨论来了解历史。

我不知道关于票据有任何“挂起”状态,除了Rich Hickey在一则评论中明确表示他想要等待一段时间再做出更改的一两个票据。有些是提交者选择要工作的票据,有些是审查者选择审查的票据。

0

评论者:alexsiller

我很希望看到这个票据上的更新补丁,该补丁专门解决varargs问题,不依赖于其他提到的票据和补丁(优先级较低)。

0

评论者:ragge

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

该补丁采用以下方法:

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

我对第二部分的实现不是很有信心。它可能会在将来打开一些难以理解的错误。一个选项是更加谨慎,并确保它真的是最后一个参数,例如,或者甚至传递{{Method}}对象(或变长参数标志)以让我们真正知道我们可以期望和需要做什么。

0

评论者:ragge

我意识到我的补丁遗漏了两个重要案例;在Reflector中的接口处理以及在多个匹配方法中的处理。我将研究这个问题,但仍然期待对{{MethodExpr/emitTypedArgs}}中方法的反馈。

0

评论者:alexsiller

我赞成使用isVarArg()显式地处理这种案例,而不是猜测我们是否处于这种情况。我们应该检查调用具有过多参数的var args方法(参数不足、过多等)的行为,并在看来需要的地方添加测试。并且还要确保非varargs的案例没有发生改变。

此外,请注意,根据通用规则,现有的AOT编译代码可能依赖于对公开Reflector方法的调用,因此如果您更改了公开Reflector方法的签名,那么应该保留一个具有旧arity的版本,以便向后兼容。

0
by

评论者: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
by

评论者: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
by
参考: https://clojure.atlassian.net/browse/CLJ-440 (由ataggart报告)
...