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

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

本文介绍了通过多个对象传递隐式参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I wonder is it possible to pass implicit params through singletons like that

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而受到影响,那么有什么办法可以做类似的事情吗?也许有一种方法可以为对象设置一个隐式,但要在其外部?

Bar.greetBar doesn't affected by implicit value in main, but I want it to be affected without passing implicit param to greetBar, so is there any way to do something like that? Maybe there is a way to set an implicit for object but in outer of it?

推荐答案

您应在方法中添加隐式参数

You should add implicit parameter to the method

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

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

或将对象设为类,并向该类添加隐式参数

or make the object a class and add implicit parameter to the 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")),则应取消注释.

I commented out default value /*= Greet("Hello")*/. If you want greetBar not to compile when there is no implicit in scope then you should keep it commented out. If you want behavior similar to greet (i.e. Greet("Hello") when there is no implicit in scope) then you should uncomment it.

请注意,如果在伴随对象中隐式定义了较低优先级,则可以避免重复默认值

Please notice that you can avoid repeating default value if you define lower-priority implicit in companion object

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

另请参阅如何使用Scala中的另一种方法?

原则上,您可以使用宏注释 (但您不应该)

In principle, you can do this with a macro annotation (but you shouldn't)

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$1: 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$2: Greet = Greet("Hello")) = Foo.greet("Bar");
//  def xxx(i: Int)(implicit greet$macro$2: Greet = Greet("Hello")) = $qmark$qmark$qmark;
//  def yyy(i: Int)(implicit s: String, greet$macro$2: Greet = Greet("Hello")) = $qmark$qmark$qmark
//}

这篇关于通过多个对象传递隐式参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-18 12:33