24 Stimmen

Wie disambiguate ich in Scala zwischen Methoden mit vararg und ohne

Ich versuche, die java jcommander-Bibliothek von Scala zu verwenden. Die Java JCommander-Klasse hat mehrere Konstruktoren:

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

Ich möchte den ersten Konstruktor aufrufen, der die keine varargs. Ich habe es versucht:

jCommander = new JCommander(cmdLineArgs)

Ich erhalte die Fehlermeldung:

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)

Ich habe auch versucht, einen benannten Parameter zu verwenden, erhielt aber das gleiche Ergebnis:

jCommander = new JCommander(`object` = cmdLineArgs)

Wie sage ich Scala, dass ich den Konstruktor aufrufen möchte, der keine varargs annimmt?

Ich verwende Scala 2.8.0.

18voto

Daniel C. Sobral Punkte 290004

Entschuldigung, ich weiß jetzt, dass dies ein bekanntes Interoperabilitätsproblem mit Java ist. Siehe diese Frage y das Ticket . Die einzige Lösung, die ich kenne, ist die Erstellung einer kleinen Java-Klasse, um diese Aufrufe zu disambiguieren.

11voto

Rex Kerr Punkte 164629

Die einzige Scala-Lösung für dieses Problem, die ich kenne, beinhaltet Reflection.

Zweideutige Methoden

Nehmen wir an, wir haben eine Java-Testklasse:

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

Wir können über die Reflexion direkt auf die Methode zugreifen:

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]

oder wir können Strukturtypen verwenden (die unter der Haube Reflexion verwenden), um das Gleiche mit weniger Boilerplate zu erreichen:

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

Zweideutige Konstrukteure

Unsere Strategie muss anders aussehen, da wir zunächst kein Objekt haben. Nehmen wir an, wir haben die Java-Klasse

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

Der Ansatz der Strukturtypen funktioniert nicht mehr, aber wir können immer noch die Reflexion verwenden:

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

5voto

Alain O'Dea Punkte 19581

Ich denke, die einfachste Möglichkeit ist, eine Java-Klasse mit einer Factory-Methode zu haben, um das Problem zu überbrücken:

package com.beust.jcommander;

public class JCommanderFactory {
    public static createWithArgs(Object cmdLineArgs) {
        return new JCommander(cmdLineArgs);
    }
}

Alternativ können Sie auch Folgendes verwenden http://jewelcli.sourceforge.net/usage.html stattdessen. JewelCli verfügt über eine eindeutige Factory-Methode für den gleichen Zweck und verwendet auch die PICA-Technik (Proxied Interfaces Configured with Annotations) http://www.devx.com/Java/Article/42492/1954 .

Ich habe sogar eine Beispiel für die Verwendung von JewelCLI mit Scala hier auf Stack Overflow .

2voto

Yawar Punkte 10588

Der Weg, diese Mehrdeutigkeit zu vermeiden, besteht darin, den Compiler zu zwingen, die Überladung auszuwählen, die mehr als ein Argument annimmt, indem man die Scala-Syntax zur Sammlungsexplosion verwendet, um eine Singleton-Sammlung zu übergeben:

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

1voto

Teimuraz Punkte 7895

Sie können den Konstruktor mit varags aufrufen, aber eine leere Liste von varags übergeben.

(Natürlich, wenn Sie wissen, dass die Konstruktion von JCommander mit leeren Varags das gleiche Ergebnis liefert wie der Aufruf des überladenen Konstruktors (oder der Methode) ohne Vargs)

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X