Как предоставить значение по умолчанию для неявных параметров на уровне класса
Я пытаюсь определить класс с некоторыми методами, принимающими неявный параметр:
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
класс, например.
2 ответа
Я нашел обходной путь:
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")
, из-за синтаксиса странности, объясненной здесь.
Это плохая идея использовать такой общий тип как 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")