Как предоставить значение по умолчанию для неявных параметров на уровне класса
Я пытаюсь определить класс с некоторыми методами, принимающими неявный параметр:
object Greetings {
def say(name: String)(implicit greetings: String): String = greetings + " " +name
}
Я использую этот класс из другого класса
implicit val greetings = "hello" //> greetings : java.lang.String = hello
Greetings.say("loic") //> res0: String = hello loic
Greetings.say("loic")("hi") //> res1: String = hi loic
Моя проблема в том, что она работает, только если я определяю неявный val вне моего объекта Greetings.
Я хотел бы иметь возможность предоставлять методы с неявными параметрами со значением по умолчанию внутри моего класса, чтобы упростить использование моего API (например, API-интерфейс Scala).
Итак, я хотел бы сделать это, но он не работает (неявное значение не найдено):
object Greetings {
implicit val greetings = "hello"
def say(name: String)(implicit greetings: String): String = greetings + " " +name
}
а затем
Greetings.say("loic")
Greetings.say("loic")("hi")
Я знаю, что могу определить значение по умолчанию с помощью (implicit greetings: String = "hello")
, но я хотел бы сделать это на уровне класса, чтобы избежать повторения, если существует много методов.
Я предполагаю, что у меня что-то не хватает, потому что я видел, что CanBuildFrom
определяется внутри класса List
, например.
Ответы
Ответ 1
Я нашел обходное решение:
class Greetings(implicit val greetings: String = "hello") {
def say(name: String): String = greetings + " " + name
}
Подобно этому, я могу иметь значение по умолчанию и переопределять его, если хочу:
new Greetings().say("loic") //> res0: String = hello loic
implicit val greetings = "hi" //> greetings : java.lang.String = hi
new Greetings().say("loic") //> res1: String = hi loic
new Greetings()("coucou").say("loic") //> res2: String = coucou loic
Примечание: new Greetings()("coucou")
работает, а не new Greetings("coucou")
, из-за синтаксической странности, объясненной здесь.
Ответ 2
Плохая идея использовать такой общий тип, как String
в неявной.
Основная причина заключается в том, что неявный поиск основан исключительно на типе, так что, если кто-то еще определяет другое неявное значение типа String? Вы можете столкнуться с конфликтом. Поэтому вы должны определить свой собственный тип для своей собственной цели (простая оболочка вокруг String).
Другая причина заключается в том, что при поиске неявных значений компилятор будет искать (среди других мест) в сопутствующем объекте (если есть) типа неявного значения. Вы можете легко увидеть, насколько это полезно, поскольку объект-компаньон является естественным местом для размещения неявного значения по умолчанию (как в вашем случае). Но если неявное значение имеет тип, который у вас нет (например, String
), вы просто не можете написать для него объект-компаньон, тогда как с вашим собственным типом-оболочкой проблем нет.
Хорошо, достаточно слов, вот как вы можете это сделать:
case class Greetings( value: String ) {
override def toString = value
}
object Greetings {
// this implicit is just so that we don't have to manually wrap
// the string when explicitly passing a Greetings instance
implicit def stringToGreetings( value: String ) = Greetings( value )
// default implicit Greetings value
implicit val greetings: Greetings ="hello"
def say(name: String)(implicit greetings: Greetings): String = greetings + " " +name
}
Greetings.say("loic")
Greetings.say("loic")("hi")