Список параметров ("*") с ленивыми параметрами "по имени"?
Я могу:
scala> def foo( f: => String) = println(f)
foo: (f: => String)Unit
и я могу:
scala> def foo( f: String*) = f.map(println)
foo: (f: String*)Seq[Unit]
но я не могу
scala> def foo( f: =>String* ) = f.map(println)
<console>:1: error: ')' expected but identifier found.
def foo( f: =>String* ) = f.map(println)
^
ни
scala> def foo( f: (=>String)* ) = f.map(println)
<console>:1: error: no by-name parameter type allowed here
def foo( f: (=>String)* ) = f.map(println)
^
Есть ли другой способ сделать то, что я хочу? Почему это не разрешено?
1 ответ
Здесь =>
означает, что параметр передается по имени. То, что вы пробовали, не допускается просто потому, что =>String
не фактический тип (в отличие от сказать ()=>String
) и вы не можете сделать Array[=>String]
, Учитывая, что переменный параметр x: T*
находится под капотом, обрабатывается как массив, содержащий все значения параметра (как в x: Array[T]
), это причина, почему не в состоянии создать Array[=>String]
(что не имеет смысла) также означает, что переменный параметр f: (=>String)*
это невозможно.
Эту проблему можно обойти, используя небольшой класс-оболочку:
implicit class ByName[T]( getValue: => T ) extends Proxy {
def apply(): T = getValue
def self = apply()
}
Затем измените подпись вашего метода следующим образом:
def foo( fs: ByName[String]* )
При вызове вашего метода с несколькими аргументами все аргументы будут неявно заключены в ByName
экземпляр, а затем вы можете позвонить apply
чтобы получить действительное значение:
def foo( fs: ByName[String]* ) = fs foreach { f => println( f() ) }
При условии ByName
продолжается Proxy
для простых вещей, таких как вызов toString
или тестирование равенства вам даже не нужно звонить apply
, Таким образом, вы можете просто сделать:
def foo( fs: ByName[String]* ) = f foreach println