scala 如何在类级别为隐式参数提供默认值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12767074/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 04:36:30  来源:igfitidea点击:

How to provide default value for implicit parameters at class level

scala

提问by Loic

I'm trying to define a class with some methods taking an implicit parameter :

我正在尝试使用一些带有隐式参数的方法来定义一个类:

object Greetings {
  def say(name: String)(implicit greetings: String): String = greetings + " " +name 
}

I use this class from another class

我从另一个班级使用这个班级

implicit val greetings = "hello"                //> greetings  : java.lang.String = hello
Greetings.say("loic")                           //> res0: String = hello loic
Greetings.say("loic")("hi")                     //> res1: String = hi loic

My problem is that it works only if I define the implicit val outside my Greetings object. I would like to be able to provide methods with implicit parameters, with a default value inside my class, to make easier the use of my API (like Scala collection API).

我的问题是它只有在我在 Greetings 对象之外定义隐式 val 时才有效。我希望能够提供带有隐式参数的方法,在我的类中使用默认值,以便更轻松地使用我的 API(如 Scala 集合 API)。

So I would like to do this, but it's not working (implicit value not found) :

所以我想这样做,但它不起作用(找不到隐式值):

object Greetings {
  implicit val greetings = "hello"    
  def say(name: String)(implicit greetings: String): String = greetings + " " +name 
}

and then

接着

Greetings.say("loic")                         
Greetings.say("loic")("hi") 

I know I can define a default value with (implicit greetings: String = "hello")but I would like to do it at class level, to avoid repeating if there are many methods.

我知道我可以定义一个默认值,(implicit greetings: String = "hello")但我想在类级别这样做,以避免在有很多方法时重复。

I guess I'm missing something because I saw that CanBuildFromis defined inside the Listclass, for example.

我想我遗漏了一些东西,因为我看到它CanBuildFrom是在List类中定义的,例如。

采纳答案by Loic

I've found a workaround:

我找到了一个解决方法:

class Greetings(implicit val greetings: String = "hello") {
    def say(name: String): String = greetings + " " + name 
}

Like this I can have a default value and override it if I want:

像这样,我可以有一个默认值并根据需要覆盖它:

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

Note: new Greetings()("coucou")is working, not new Greetings("coucou"), because of a syntax strangeness explained here.

注意:new Greetings()("coucou")正在工作,而不是new Greetings("coucou"),因为这里解释的语法奇怪。

回答by Régis Jean-Gilles

It is a bad idea to use such a general type as Stringin an implicit. The main reason is that implicit lookup is solely base on the type, so what if someone else defines another implicit value of type String? You might end up with a conflict. So you should define your own specific type for your own purpose (a simple wrapper around String).

String在隐式中使用这种通用类型是一个坏主意。主要原因是隐式查找完全基于类型,那么如果其他人定义了另一个字符串类型的隐式值呢?你可能会以冲突告终。因此,您应该为自己的目的定义自己的特定类型(字符串的简单包装器)。

Another reason is that when looking for implicit values, the compiler will look (among other places) into the companion object (if any) of the implicit value type. You can easily see how useful it is, as the companion object is the natural place to put a default implicit value (as in your case). But if the implicit value is of a type that you don't own (such as String) you just cannot write a companion object for it, while with your own wrapper type there is no problem.

另一个原因是,在查找隐式值时,编译器会(在其他地方)查找隐式值类型的伴随对象(如果有)。您可以很容易地看到它是多么有用,因为伴随对象是放置默认隐式值的自然位置(如您的情况)。但是,如果隐式值是您不拥有的类型(例如String),您就无法为其编写伴随对象,而使用您自己的包装器类型则没有问题。

OK, enough verbiage, here is how you can do it:

好吧,废话太多了,这里是你可以这样做的方法:

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")