How do I disambiguate in Scala between methods with vararg and without

后端 未结 5 1749
无人及你
无人及你 2020-11-30 08:30

I\'m trying to use the java jcommander library from Scala. The java JCommander class has multiple constructors:

 public JCommander(Object object)  
 publi         


        
相关标签:
5条回答
  • 2020-11-30 09:09

    I think your easiest option is to have a Java class with a factory method to bridge the issue:

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

    Alternatively you could use http://jewelcli.sourceforge.net/usage.html instead. JewelCli has an unambiguous factory method for the same purpose and also uses PICA (Proxied Interfaces Configured with Annotations) technique http://www.devx.com/Java/Article/42492/1954.

    In fact I have an example of using JewelCLI with Scala here on Stack Overflow.

    0 讨论(0)
  • 2020-11-30 09:19

    The way to avoid this ambiguity is to force the compiler to pick the overload that takes more than one argument, using Scala's collection explosion syntax to pass in a singleton collection:

    import java.util.stream.Stream
    val stream = Stream.of(List(1):_*)
    
    0 讨论(0)
  • 2020-11-30 09:30

    The only Scala solution to this problem that I know involves reflection.

    Ambiguous Methods

    Let's suppose we have a Java test class:

    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; }
    }
    

    We can get access to the method via reflection directly:

    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]
    

    or we can use structural types (which use reflection under the hood) to achieve the same thing with less boilerplate:

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

    Ambiguous Constructors

    Our strategy has to differ because we don't actually have an object to begin with. Let's suppose we have the Java class

    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; }
    }
    

    The structural types approach no longer works, but we can still use reflection:

    val mkAmbig2 = classOf[Ambig2].getConstructors.filter(_.getParameterTypes.length==1)
    val ambig = mkAmbig2.head.newInstance(Some(5)).asInstanceOf[Ambig2]
    ambig.say   // Some(5)
    
    0 讨论(0)
  • 2020-11-30 09:33

    You can call the constructor with varags, but pass an empty list of varags.

    (Of course, if you know that constructing JCommander with empty varags will produce the same result as calling the overloaded constructor (or method) without vargs)

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

    0 讨论(0)
  • 2020-11-30 09:34

    Sorry, I now realize this is a known interoperability problem with Java. See this question and the ticket. The only work around I know of is to create a small Java class just to disambiguate these calls.

    0 讨论(0)
提交回复
热议问题