通过多个对象传递隐式参数

Pass implicit parameter through multiple objects

我想知道是否可以像那样通过单例传递隐式参数

case class Greet(g: String)

object Foo {
  def greet(name: String)(implicit greet: Greet = Greet("Hello")) = println(greet.g + " " + name)
}

object Bar {
  def greetBar = Foo.greet("Bar")
}


object Main {
  def main(args: Array[String]): Unit = {
    implicit val greet: Greet = Greet("Goodbye")
    
    Foo.greet("Sunshine") // Goodbye Sunshine
    Bar.greetBar // Hello Bar
  }
}

Bar.greetBar 不受 main 中隐式值的影响,但我希望它在不将隐式参数传递给 greetBar 的情况下受到影响,所以有什么办法可以做某事像那样?也许有一种方法可以为对象设置隐式但在它的外部?

您应该向方法添加隐式参数

object Bar {
  def greetBar(implicit greet: Greet /*= Greet("Hello")*/) = Foo.greet("Bar")
}

implicit val greet: Greet = Greet("Goodbye")
Bar.greetBar // Goodbye Bar

或者使对象成为 class 并将隐式参数添加到 class

class Bar(implicit greet: Greet /*= Greet("Hello")*/) {
  def greetBar = Foo.greet("Bar")
}

implicit val greet: Greet = Greet("Goodbye")
(new Bar).greetBar // Goodbye Bar

我注释掉了默认值/*= Greet("Hello")*/。如果您希望 greetBar 在范围内没有隐式时不编译,那么您应该将其注释掉。如果你想要类似于 greet 的行为(即 Greet("Hello") 当范围内没有隐含时)那么你应该取消注释它。

请注意,如果在伴生对象中定义 lower-priority 隐式

,则可以避免重复默认值
case class Greet(g: String)
object Greet {
  implicit val lowPriorityGreet: Greet = Greet("Hello")
}

object Foo {
  def greet(name: String)(implicit greet: Greet) = println(greet.g + " " + name)
}

object Bar {
  def greetBar(implicit greet: Greet) = Foo.greet("Bar")
}
// class Bar(implicit greet: Greet) {
//   def greetBar = Foo.greet("Bar")
// }

implicit val greet: Greet = Greet("Goodbye")

Foo.greet("Sunshine") // Goodbye Sunshine
Bar.greetBar // Goodbye Bar
// (new Bar).greetBar // Goodbye Bar

另请参阅

I want to do this to set Greet implict for all methods in Bar

原则上,您可以使用 macro annotation(但您不应该这样做)

import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

@compileTimeOnly("enable macro annotations")
class greetAware extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro GreetAwareMacro.impl
}

object GreetAwareMacro {
  def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
    import c.universe._

    val greet = TermName(c.freshName("greet"))
    val implicitGreet = q"""implicit val $greet: Greet = Greet("Hello")"""

    def isImplicit(param: Tree): Boolean = param match {
      case q"$mods val $_: $_ = $_" => mods.hasFlag(Flag.IMPLICIT)
    }

    annottees match {
      case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
        val body1 = body.map {
          case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" =>
            val paramss1 =
              if (paramss.nonEmpty && paramss.last.nonEmpty && isImplicit(paramss.last.head))
                paramss.init :+ (paramss.last :+ implicitGreet)
              else paramss :+ List(implicitGreet)
            q"$mods def $tname[..$tparams](...$paramss1): $tpt = $expr"
          case notMethod => notMethod
        }
        q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body1 }"
    }
  }
}

用法:

@greetAware
object Foo {
  def greet(name: String) = println(implicitly[Greet].g + " " + name)
}

@greetAware
object Bar {
  def greetBar = Foo.greet("Bar")
  def xxx(i: Int) = ???
  def yyy(i: Int)(implicit s: String) = ???
}

implicit val greet: Greet = Greet("Goodbye")

Foo.greet("Sunshine") // Goodbye Sunshine
Bar.greetBar // Goodbye Bar

//scalac: object Foo extends scala.AnyRef {
//  def <init>() = {
//    super.<init>();
//    ()
//  };
//  def greet(name: String)(implicit greet$macro: Greet = Greet("Hello")) = println(implicitly[Greet].g.$plus(" ").$plus(name))
//}
//scalac: object Bar extends scala.AnyRef {
//  def <init>() = {
//    super.<init>();
//    ()
//  };
//  def greetBar(implicit greet$macro: Greet = Greet("Hello")) = Foo.greet("Bar");
//  def xxx(i: Int)(implicit greet$macro: Greet = Greet("Hello")) = $qmark$qmark$qmark;
//  def yyy(i: Int)(implicit s: String, greet$macro: Greet = Greet("Hello")) = $qmark$qmark$qmark
//}