groovy null 安全运算符,识别什么是 null?

raf*_*ian 5 groovy

Groovy 中的 null 安全运算符非常适合减少代码并使代码更具可读性。我们可以从这里开始:

def customer = getCustomer(custNo)
if(!customer)
   throw new Exception("Invalid customer: ${custNo}")

def policy = customer.getPolicy(policyNo)
if(!policy)
   throw new Exception("Invalid policy: ${policyNo}")

def claim = policy.getClaim(claimNo)
if(!claim)
   throw new Exception("Invalid claim: ${claimNo}")
Run Code Online (Sandbox Code Playgroud)

..对此...

def claim = getCustomer(custNo)?.getPolicy(policyNo)?.getClaim(claimNo)
Run Code Online (Sandbox Code Playgroud)

但没有什么是免费的;使用 null/安全导航,如果claim为 null,则不会立即明显看出导致它的原因custNopolicyNoclaimNo可能无效。

我们可以返回并开始检查什么是 null,但这会适得其反,实际上,这是不可能的,因为中间对象不存储在变量中。

所以问题是:当使用 null/安全导航链接方法调用时是否可以识别什么是 null?

更新

我使用动态方法调用再次尝试了这一点。它需要一个 init 目标(通常是一个 dao)来初始化对象(在本例中为 customer),以及一个包含方法名称作为字符串(参数作为值)的映射。使用迭代器,invokeChain简单地遍历映射(链);如果链中的任何内容返回 null,那么识别导致它的方法就变得微不足道。

  def invokeChain = { initTarget, chain ->
     def obj
     chain.eachWithIndex{ it, idx ->
        //init obj from dao on first iteration only,
        //remaining iterations get from obj itself
        obj = (!idx) ? initTarget."$it.key"(it.value) : obj?."$it.key"(it.value)            

        if(!obj)
           throw new Exception("${it.key}(${it.value}) returned null")           
     }
     obj
  }
Run Code Online (Sandbox Code Playgroud)

用法

模拟customerdao for initTarget...我已插入null作为返回类型 for getClaim(),这应该引发异常。

   def static getCustomer = { custNo ->
      [ getPolicy: { p ->           
            [getClaim:{ c ->
                  null //"Claim #${c}"
               }]
         }]
   }
Run Code Online (Sandbox Code Playgroud)

..使用invokeChain,简单易行:

  def claim = invokeChain(this, [getCustomer:123, getPolicy:456, getClaim:789])
Run Code Online (Sandbox Code Playgroud)

...按照预期抛出异常:

Exception in thread "main" java.lang.Exception: getClaim(789) returned null
Run Code Online (Sandbox Code Playgroud)

我喜欢这种方法,因为它紧凑、可读且易于使用;你怎么认为?

Sea*_*ull 4

我认为没有明确的方法可以做到这一点。我可能是错的,稍后会检查来源,但安全导航是 if 语句的语法糖。

作为一种黑客,您可以使用拦截器包装代码,跟踪内部的最后一个方法调用,然后使用该信息提供错误消息。

它不会便宜,并且会花费你一些代码来实现拦截和运行时的一些性能。但你可以实现类似的目标

    mayFail("getCusomer", "getPolicy", "getClaim") {
         getCustomer(custNo)?.getPolicy(policyNo)?.getClaim(claimNo)
    } == "getPolicy" // failed on second step
Run Code Online (Sandbox Code Playgroud)

编辑:正如@tim_yates所证明的那样,?.它是一个带有 if 构造的语法糖。感谢Vorg van Geir提供的链接,我已将其复制到此处作为答案。他说,这已经过时了,看来他是对的。我已经成功地使 ProxyMetaClass 工作(在 Groovy 2.0.6 中),所以给定的方式并没有完全破坏。现在我需要指定要拦截的确切类,并且我找不到捕获继承方法调用的方法。(简单地拦截java.lang.Object

def logInterceptor = new TracingInterceptor()
logInterceptor.writer = new StringWriter()

def intProxy = ProxyMetaClass.getInstance(Integer)
def stringProxy = ProxyMetaClass.getInstance(String)
intProxy.setInterceptor(logInterceptor)
stringProxy.setInterceptor(logInterceptor)
intProxy.use {
    stringProxy.use {
       println(("Hello" + "world").size().hashCode())
}   }

println(logInterceptor.writer.toString())
Run Code Online (Sandbox Code Playgroud)

所有这些地狱可能都包含在一些实用程序代码中,但我非常怀疑这样做的必要性。性能开销将非常糟糕,并且一些样板代码将保留。

游戏得不偿失。