【问题标题】:Java Bug with ambiguous methods using varargs?使用可变参数的方法不明确的 Java 错误?
【发布时间】:2014-03-05 05:16:54
【问题描述】:

我有一个类有两个这样的方法:

public class Dummy{
  public void doIt(String arg1, File arg2, Writer... ctx){
    // Do something very important...
  }

  public void doIt(String arg1, Writer... ctx){
    // Do something else...
  }

  public static void main(String[] args){
    new Dummy().doIt("Test", null);
  }
}

我希望编译器会给出错误,因为方法调用是模棱两可的。而是调用第二种方法。

在我们的例子中,模棱两可的方法是从数据库方法和稍后添加的可变参数生成的。现在我们不知道如何避免示例中的方法调用问题。

有没有其他人有这个问题以及如何解决它的想法?

【问题讨论】:

  • 如果要使用第二种方法,请使用new Dummy().doIt("Test", null, null);。 Java 将优先考虑最合适的方法,并且由于 varargs 整体上是可选的(没有参数是有效的 varargs 调用),所以前者更具体。如果您想知道确定最合适方法的确切规则,那么您必须查看 JLS。
  • @user3284827: 你用的是什么编译器?使用 JDK 7,我已经收到有关该调用的警告。要调用第一个方法,只需将 null 转换为 File
  • JLS § 15.12 是这方面的权威,但它非常庞大且难以理解。
  • 答案可能在 JLS 15.12.2.5 中,如果适用,则选择最具体的方法;稍后它讨论了它如何确定一种方法是否比另一种方法更具体用于可变参数方法。不过,我还不明白它是如何工作的。这是相当强大的。但我怀疑答案就在这部分。
  • 这些方法做这些不同的事情吗?当你说它们是从数据库方法生成的时候,它让我想到了一个持久性框架。如果目标是插入或更新一行,那么这两种方法不会导致第一个参数的值和未指定的其余参数的值吗?或者,换一种说法,你能否在这种情况下使语义相同,以便编译器选择哪种方法无关紧要?

标签: java compiler-construction variadic-functions ambiguous


【解决方案1】:

我建议创建一个明确的包装类,以防止意外滥用生成的类。

示例包装器:

public class NicerDummy{
    Dummy dummy;
    public NicerDummy(){
        dummy = new Dummy(); 
    }

    public void doSomething(String arg1, Writer... ctx){
        dummy.doIt(arg1, Writer);
    }

    public void doSomethingElse(String arg1, File arg2, Writer... ctx){
        dummy.doIt(arg1, arg2, ctx);
    }
}

【讨论】:

    【解决方案2】:

    您的具体问题的答案是照别人说的做,并调用doIt("someString", null, null),使方法调用更具体。

    话虽如此,真正的答案是重写方法以将 Collection 作为参数。使用可变参数往往会导致比它解决的问题更多。

    【讨论】:

      【解决方案3】:

      我根据这个给出答案:

      问题是如何找到这样的方法调用,第一个 方法应该被调用(并且是在我们改变我们的代码之前 生成器),但调用了第二个。

      首先,正如已经指出的那样,Java 编译器会针对此类方法的使用发出警告。它看起来像这样:

      com/stack/undsprlbl/varargs/Main.java:10:警告:非可变参数调用 最后一个参数的参数类型不准确的 varargs 方法;

      并且可以很容易地从javac 输出得到grep-ed。

      其次,您可以考虑为您的代码编写一些自测:

          Class cl = Ambiguity.class;
          Method[] methods = cl.getDeclaredMethods();
          Set<String> varargsMethods = new HashSet<String>();
          for (Method method : methods) {
              Class c[] = method.getParameterTypes();
              if(c.length > 0)
              {
                  Class last = c[c.length - 1];
                  if(last.isArray())
                  {
                      if(varargsMethods.contains(method.getName()))
                          System.out.println("Method " + cl.getName() + "#"+ method.getName() + " looks suspicious.");
                      else
                          varargsMethods.add(method.getName());
                  }
              }
          }
      

      理解您应该迭代所有类而不是直接提及。这个answer 似乎是一个可行的方法 --- 获取应用程序中的所有包并检查它们。

      此时您将有两个列表:

      1. 不明确的可变参数方法用法列表

      2. 不明确的可变参数方法列表。

      通过这两者,您可以找出您可能会遇到问题的地方。

      接下来,我建议在添加第二个方法之前检查代码的版本,并找出第一个方法的所有用法。这些显然必须消除歧义,以支持 HEAD 版本中的第一种方法。这样一来,我想,剩下的电话数量就会非常有限。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多