Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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

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

I want to to call the first constructor that takes no varargs. I tried:

jCommander = new JCommander(cmdLineArgs) 

I get the error:

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) 

I've also tried using a named parameter, but got the same result:

jCommander = new JCommander(`object` = cmdLineArgs) 

How do I tell Scala I want to call the constructor that doesn't take varargs?

I'm using Scala 2.8.0.

like image 964
Brian Pugh Avatar asked Jul 22 '10 22:07

Brian Pugh


2 Answers

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.

like image 110
Daniel C. Sobral Avatar answered Oct 19 '22 15:10

Daniel C. Sobral


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) 
like image 43
Rex Kerr Avatar answered Oct 19 '22 15:10

Rex Kerr