加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 如何开发宏来短路null?

发布时间:2020-12-16 09:53:09 所属栏目:安全 来源:网络整理
导读:在斯卡拉,如果我有 hub = myBicycle.getFrontWheel.getHub() 并且可能缺少前轮,即myBicycle.getFrontWheel()== null,我只想在这种情况下将hub指定为null,表达它的最简洁方法是什么? 我现在不得不这样做 hub = if (myBicycle.getFrontWheel() == null) null
在斯卡拉,如果我有

hub = myBicycle.getFrontWheel.getHub()

并且可能缺少前轮,即myBicycle.getFrontWheel()== null,我只想在这种情况下将hub指定为null,表达它的最简洁方法是什么?

我现在不得不这样做

hub = if (myBicycle.getFrontWheel() == null) null else myBicycle.getFrontWheel.getHub()

当访问者链更长时,它会变得更糟.

不熟悉Scala宏,我想知道是否有可能编写一个Scala宏以某种方式捕获方法名称并仅在对象引用为非null时才应用它?

解决方法

实际上,我最近写的是这样一个宏,受到 a question about null-safe dereferences in Scala的启发.顺便说一下,这是一个非常类似的问题,并且包含了一个很长的讨论,你可以做些什么来实现这一点,包括使用Option,花哨的捕获方式NPE等.

我写的一些关于宏的评论(来源包括在下面):

>它返回一个Option,当某个时候存在空取消引用时它将是None.
>它使用if-else将每个成员访问点转换为空值保护访问,当前缀为null时返回None.
>它有点复杂,我想象它…
>有一些不适用的极端情况,我所知道的是使用getClass方法 – 这是由编译器特别针对其返回类型处理的.
>隐式转换可能存在不一致.想象一下,你有一个表达式a.b和b是通过隐式转换达到的,所以有效地说,这个表达式就像conv(a).b.现在,问题出现了:我们应该检查a是null还是conv(a)是null还是两者都是?目前,我的宏只检查a是否为null,因为它对我来说似乎更自然,但在某些情况下这可能不是理想的行为.此外,在我的宏中检测隐式转换是一个小问题,在this question中描述.

宏:

def withNullGuards[T](expr: T): Option[T] = macro withNullGuards_impl[T]

def withNullGuards_impl[T](c: Context)(expr: c.Expr[T]): c.Expr[Option[T]] = {
  import c.universe._

  def eqOp = newTermName("==").encodedName
  def nullTree = c.literalNull.tree
  def noneTree = reify(None).tree
  def someApplyTree = Select(reify(Some).tree,newTermName("apply"))

  def wrapInSome(tree: Tree) = Apply(someApplyTree,List(tree))

  def canBeNull(tree: Tree) = {
    val sym = tree.symbol
    val tpe = tree.tpe

    sym != null &&
      !sym.isModule && !sym.isModuleClass &&
      !sym.isPackage && !sym.isPackageClass &&
      !(tpe <:< typeOf[AnyVal])
  }

  def isInferredImplicitConversion(apply: Tree,fun: Tree,arg: Tree) =
    fun.symbol.isImplicit && (!apply.pos.isDefined || apply.pos == arg.pos)

  def nullGuarded(originalPrefix: Tree,prefixTree: Tree,whenNonNull: Tree => Tree): Tree =
    if (canBeNull(originalPrefix)) {
      val prefixVal = c.fresh()
      Block(
        ValDef(Modifiers(),prefixVal,TypeTree(null),prefixTree),If(
          Apply(Select(Ident(prefixVal),eqOp),List(nullTree)),noneTree,whenNonNull(Ident(prefixVal))
        )
      )
    } else whenNonNull(prefixTree)

  def addNullGuards(tree: Tree,whenNonNull: Tree => Tree): Tree = tree match {
    case Select(qualifier,name) =>
      addNullGuards(qualifier,guardedQualifier =>
        nullGuarded(qualifier,guardedQualifier,prefix => whenNonNull(Select(prefix,name))))
    case Apply(fun,List(arg)) if (isInferredImplicitConversion(tree,fun,arg)) =>
      addNullGuards(arg,guardedArg =>
        nullGuarded(arg,guardedArg,prefix => whenNonNull(Apply(fun,List(prefix)))))
    case Apply(Select(qualifier,name),args) =>
      addNullGuards(qualifier,prefix => whenNonNull(Apply(Select(prefix,args))))
    case Apply(fun,args) =>
      addNullGuards(fun,guardedFun => whenNonNull(Apply(guardedFun,args)))
    case _ => whenNonNull(tree)
  }

  c.Expr[Option[T]](addNullGuards(expr.tree,tree => wrapInSome(tree)))
}

编辑

这是一段额外的代码,使语法更好:

def any2question_impl[T,R >: T](c: Context {type PrefixType = any2question[T]})(default: c.Expr[R]): c.Expr[R] = {
  import c.universe._

  val Apply(_,List(prefix)) = c.prefix.tree
  val nullGuardedPrefix = withNullGuards_impl(c)(c.Expr[T](prefix))
  reify {
    nullGuardedPrefix.splice.getOrElse(default.splice)
  }
}

implicit class any2question[T](any: T) {
  def ?[R >: T](default: R): R = macro any2question_impl[T,R]
}

最后,您可以使用以下代码:

val str1: String = "hovercraftfullofeels"
val result1 = str1.substring(3).toUpperCase ? "THERE WAS NULL"
println(result1) // prints "ERCRAFTFULLOFEELS"

val str2: String = null
val result2 = str2.substring(3).toUpperCase ? "THERE WAS NULL"
println(result2) // prints "THERE WAS NULL"

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读