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)

方法类提供了一个{{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

评论由: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',只有几个块无法无过错地应用。请参阅此处JIRA工作流程页的“更新过时的补丁”部分:http://dev.clojure.org/display/design/JIRA workflow

0

评论由:jafingerhut发表

啊。删除了附件,因为它是为了CLJ-445,或者至少它是这样命名的。CLJ-445肯定有一个长长的评论历史,所以如果其中一个补丁解决了这个问题,那么你可以通过阅读那里的讨论来查看历史。

我不知道有任何关于工单的“挂起”状态,除非Rich Hickey在评论中明确表示他想要等待一段时间再进行更改的情况。只是有些工单是贡献者选择要工作的,以及筛选者选择要筛选的。

0
by

评论者:alexmiller

我希望能看到一个针对此工单的更新补丁,该补丁专门解决了varargs问题,而不依赖于其他提到的工单和补丁(优先级较低)。

0
by

评论者:ragge

我尝试了一下,已附上一个初始补丁,其中一部分我并不是太确定/满意,所以很乐意得到反馈。

该补丁采取以下方法

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

我对第二部分实现不是很确定。这可能会在未来打开一些难以理解的错误。一个选择会是更多地进行防御,并确保它确实是最后一个参数,例如,甚至是传递{{Method}}对象(或变量长度标志)以让我们知道我们期望什么并需要做什么。

0
by

评论者:ragge

我意识到我的补丁缺少两个重要的情况;在Reflector中的接口处理以及处理多个匹配的方法。我还会调查这个问题,但仍然很愿意对{{MethodExpr/emitTypedArgs}}中的方法得到反馈。

0
by

评论者:alexmiller

我赞成使用isVarArg()来明确处理这种案例,而不是猜测我们是否处于这种情况。我们应该检查调用变量长度的方法的参数过多、过少等情况下的行为(并在需要时添加测试)。此外,还需要再次确认非变量长度的案例没有发生改变行为。

此外,请注意,作为一般规则,现有的 AOT(Ahead Of Time)编译代码可能依赖于调用公共 Reflector 方法,因此如果您更改公共 Reflector 方法的签名,则应该保留具有旧参数数且具有一些向后兼容默认行为的版本。

0
by

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

评论者:pbwolf

如果 javac 可以在调用程序不提供 "..." 分割的情况下区分方法,那么 Clojure 也应该可以。(但 javac 在特定的 "MultipleVarargMethods" 类中无法区分)

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