【问题标题】:How do I disambiguate in Scala between methods with vararg and without如何在 Scala 中消除带可变参数和不带可变参数的方法之间的歧义
【发布时间】:2010-07-22 22:13:43
【问题描述】:

我正在尝试使用 Scala 的 java jcommander 库。 java JCommander 类有多个构造函数:

 public JCommander(Object object)  
 public JCommander(Object object, ResourceBundle bundle, String... args)   
 public JCommander(Object object, String... args)   

我想调用第一个采用 no 可变参数的构造函数。我试过了:

jCommander = new JCommander(cmdLineArgs)

我得到错误:

error: ambiguous reference to overloaded definition,
both constructor JCommander in class JCommander of type (x$1: Any,x$2: <repeated...>[java.lang.String])com.beust.jcommander.JCommander
and  constructor JCommander in class JCommander of type (x$1: Any)com.beust.jcommander.JCommander
match argument types (com.lasic.CommandLineArgs) and expected result type com.beust.jcommander.JCommander
jCommander = new JCommander(cmdLineArgs)

我也尝试过使用命名参数,但得到了相同的结果:

jCommander = new JCommander(`object` = cmdLineArgs)

如何告诉 Scala 我要调用不带可变参数的构造函数?

我使用的是 Scala 2.8.0。

【问题讨论】:

    标签: java scala scalac


    【解决方案1】:

    抱歉,我现在意识到这是一个已知的 Java 互操作性问题。请参阅 this questionthe ticket。我知道的唯一解决方法是创建一个小型 Java 类来消除这些调用的歧义。

    【讨论】:

      【解决方案2】:

      据我所知,解决此问题的唯一 Scala 解决方案涉及反射。

      模棱两可的方法

      假设我们有一个 Java 测试类:

      public class Ambig {
        public Ambig() {}
        public String say(Object o) { return o.toString(); }
        public String say(Object o, String... ss) { return o.toString()+ss.length; }
      }
      

      我们可以直接通过反射访问方法:

      val ambig = new Ambig
      val methods = ambig.getClass.getMethods.filter(_.getName == "say")
      val wanted = methods.find(_.getParameterTypes.length == 1).get
      wanted.invoke(ambig, Some(5)).asInstanceOf[String]
      

      或者我们可以使用结构类型(在底层使用反射)以更少的样板实现相同的目标:

      def sayer(speaker: { def say(o: Object): String }, o: Object) = speaker.say(o)
      sayer(new Ambig, Some(5))
      

      不明确的构造函数

      我们的策略必须有所不同,因为我们实际上没有一个对象开始。假设我们有 Java 类

      public class Ambig2 {
        public final String say;
        public Ambig2(Object o) { say = o.toString(); }
        public Ambig2(Object o, String... ss) { say = o.toString()+ss.length; }
      }
      

      结构类型方法不再有效,但我们仍然可以使用反射:

      val mkAmbig2 = classOf[Ambig2].getConstructors.filter(_.getParameterTypes.length==1)
      val ambig = mkAmbig2.head.newInstance(Some(5)).asInstanceOf[Ambig2]
      ambig.say   // Some(5)
      

      【讨论】:

      • 二义性静态方法怎么样?
      【解决方案3】:

      我认为您最简单的选择是使用带有工厂方法的 Java 类来解决问题:

      package com.beust.jcommander;
      
      public class JCommanderFactory {
          public static createWithArgs(Object cmdLineArgs) {
              return new JCommander(cmdLineArgs);
          }
      }
      

      您也可以改用http://jewelcli.sourceforge.net/usage.html。 JewelCli 具有用于相同目的的明确工厂方法,并且还使用 PICA(使用注释配置的代理接口)技术http://www.devx.com/Java/Article/42492/1954

      事实上我有一个example of using JewelCLI with Scala here on Stack Overflow

      【讨论】:

        【解决方案4】:

        避免这种歧义的方法是强制编译器选择需要多个参数的重载,使用 Scala 的集合爆炸语法传入单例集合:

        import java.util.stream.Stream
        val stream = Stream.of(List(1):_*)
        

        【讨论】:

          【解决方案5】:

          您可以使用可变参数调用构造函数,但传递一个空可变参数列表。

          (当然,如果你知道用空变量构造 JCommander 将产生与不带变量调用重载构造函数(或方法)相同的结果)

          jCommander = new JCommander(cmdLineArgs, Nil: _*)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-05-13
            • 1970-01-01
            • 2015-11-09
            • 2012-03-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多