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

欢迎!请参见 关于 页面以了解更多关于这是如何工作的信息。

+18
Java Interop

问题

Clojure 对 Java vararg 方法的调用需要为最后一个参数创建一个对象数组。这是在互操作过程中常见的一个混淆源头。

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

user=> (Collections/addAll [] (object-array 0)) false user=> (Collections/addAll []) IllegalArgumentException 无匹配方法:addAll clojure.lang.Compiler$StaticMethodExpr.<init> (Compiler.java:1401)

Method 类提供了一个 {{isVarArg()}} 方法,可以用来自动告知编译器以不同方式处理。

来源:http://groups.google.com/group/clojure/browse_thread/thread/7d0d6cb32656a621

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

Java 中的 Varargs

根据目前陈述,这个问题的范围仅限于省略 varargs,但这是 Clojure 处理 varargs 与 Java 差异的一个案例。为了完整性,以下是关于 Java 如何处理 vararg 方法的一个简要概述,这可能有助于讨论 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

评论者:importer

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文件是于2010年10月23日由Alexander Taggart编写的。我只是从旧的Assembla工单跟踪系统中复制到这里以使其更容易访问。不出所料,它无法干净地应用到最新的master上。我不知道更新它需要多少工作量,但根据'patch',只有几块无法干净地应用。请参见以下JIRA工作流程页面上的“更新过时的补丁”部分:http://dev.clojure.org/display/design/JIRA workflow

0

评论者:jafingerhut

哎呀。因为附件是关于CLJ-445的,或者至少是那样命名的。CLJ-445确实有一个很长的评论历史,所以如果一个或多个其补丁解决了这个问题,那么你可以阅读那里的讨论以了解历史。

我没有了解到关于工单的“挂起”状态,除了富·希基在一两条评论中明确表示他想要等待一段时间再进行更改的情况。这只是有贡献者选择工作的工单和审阅者选择审阅的工单。

0

评论由:alexmiller

我很希望看到此工单上针对varargs问题的更新补丁,而不依赖于其他提到的工单和补丁(该补丁优先级较低)。

0

评论由:ragge

我就此问题尝试了一下,已附上初始补丁,其中一些部分我不太确定/满意,因此欢迎反馈。

该补丁采取以下方法

  1. 指导{{Reflector/getMethods}}如何找到匹配的varargs方法。除此之外,只要方法是一个可变参数方法,且方法参数的数量比所需参数多一个,它也可以匹配。这意味着这是一个可调用的可变参数方法,但用户没有提供可变参数。
  2. 在{{MethodExpr/emitTypedArgs}}中,我们处理了当被调用的方法中参数多于提供的参数数量的情况。唯一应该发生这种情况的情形是,如果这是可变参数方法,并且最后一个参数没有提供。在这种情况下,我们将在堆栈上推送新的空对象数组。

我对第二部分的具体实现不太确定。这可能会在未来引发一些难以理解的错误。一个选项是更加防御性,确保这确实是最后一个参数,例如,甚至可以传递{{Method}}对象(或可变参数标志),这样我们就“知道”可以期待什么以及需要做什么。

0

评论由:ragge

我意识到我的补丁缺少两个重要的情形;在Reflector中的接口处理和多个匹配方法的处理。我也会关注这些问题,但仍然欢迎对{{MethodExpr/emitTypedArgs}}中的方法提供反馈。

0

评论由:alexmiller

我倾向于使用isVarArg()显式处理这种情况,而不是猜测是否处于这种情况。我们应该检查调用可变参数方法的参数过多或过少时的行为(并在需要的地方添加测试),并同样检查非可变参数的情况是否未更改行为。

此外,请注意,作为一个一般规则,现有的AOT编译代码可能会调用公共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也应该可以。 (但在这特定的"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...) 都与m匹配
MultipleVarargMethods.java:6: 错误:对m的引用是模糊的

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

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

)

0
by
...