Как в Scala устранить неоднозначность между методами с vararg и без

Я пытаюсь использовать библиотеку java jcommander от Scala. Класс java JCommander имеет несколько конструкторов:

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

Я хочу вызвать первый конструктор, который не принимает никаких переменных. Я пытался:

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, что я хочу вызвать конструктор, который не принимает varargs?

Я использую Скала 2.8.0.


person Brian Pugh    schedule 22.07.2010    source источник


Ответы (5)


Извините, теперь я понимаю, что это известная проблема совместимости с Java. См. этот вопрос и билет. Единственная работа, о которой я знаю, - это создать небольшой класс Java только для устранения неоднозначности этих вызовов.

person Daniel C. Sobral    schedule 22.07.2010

Единственное известное мне решение этой проблемы на 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)
person Rex Kerr    schedule 02.06.2011
comment
Как насчет неоднозначного статического метода? - person Wins; 15.01.2014

Я думаю, что ваш самый простой вариант - иметь класс 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.

На самом деле у меня есть пример использования JewelCLI со Scala здесь, в стеке. Переполнение.

person Alain O'Dea    schedule 23.07.2010

Способ избежать этой двусмысленности — заставить компилятор выбрать перегрузку, которая принимает более одного аргумента, используя синтаксис взрыва коллекции Scala для передачи одноэлементной коллекции:

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

Вы можете вызвать конструктор с варагами, но передать пустой список варагов.

(Конечно, если вы знаете, что создание JCommander с пустыми vargs даст тот же результат, что и вызов перегруженного конструктора (или метода) без vargs)

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

person Teimuraz    schedule 07.10.2018