2

我具有以下特征和类(实际上这是一种简化,真正的代码是用 Java 编写的,不在我的控制范围内):

trait BusinessTermValue {
  def getValue: Any
}

class BusinessTermValueImpl(override val getValue: Any) extends BusinessTermValue

现在我正在尝试改进我的 API 而不触及原始代码(Pimp My Library 模式):

package object businessterms {

  implicit final class BusinessTermValueSupport(val btv: BusinessTermValue) extends AnyVal {

    def isDefined(): Boolean = btv != null && btv.value != null

    def isEmpty(): Boolean = isDefined() && (btv.value match {
      case s: String => s.isEmpty
      case l: Traversable[_] => l.isEmpty
      case c: java.util.Collection[_] => c.isEmpty
      case _ => false
    })

    def getAs[T](): T = btv.value.asInstanceOf[T]

  }

  object BusinessTerm {
    def apply(value: Any): BusinessTermValue = new BusinessTermValueImpl(value)
  }

}

它工作得很好:

println(BusinessTerm("A String").isEmpty) // false
println(BusinessTerm(1).isEmpty) // false, Int can't be empty
println(BusinessTerm(new Integer(1)).isEmpty) // false, Integer can't be empty
println(BusinessTerm(List(1, 2, 3)).isEmpty) // false
println(BusinessTerm(List(1, 2, 3).asJava).isEmpty) // false
println(BusinessTerm("").isEmpty) // true
println(BusinessTerm(List()).isEmpty) // true
println(BusinessTerm(Seq()).isEmpty) // true
println(BusinessTerm(Map()).isEmpty) // true
println(BusinessTerm(List().asJava).isEmpty) // true

仍然是模式匹配isEmpty很麻烦。理想情况下,我想使用结构类型并确保实现的任何类型都isEmpty适用于我的 API。

不幸的是,下面的代码不起作用。该变量e匹配任何类型,即使value没有定义isEmpty

def isEmpty(): Boolean = isDefined() && (btv.value match {
  case e: { def isEmpty(): Boolean } => e.isEmpty
  case _ => false
})

有没有办法isEmpty仅在底层value实现时委托?

4

2 回答 2

3

我是建议试用版的人

def isEmpty(): Boolean = isDefined && (btv.value match {
  case e: { def isEmpty(): Boolean } => Try(e.isEmpty).getOrElse(false)
  case _ => false
})

我还注意到捕获异常的成本很高,但我相信这种情况是最不常见的,因为原则上,当您希望拥有 isEmpty 方法时,您将使用此构造。所以我认为权衡可能会得到回报,并且会认为这是一个防御性编码的案例。

于 2018-04-05T15:37:38.323 回答
1

由于不受您控制的 Java 类返回Any/ Object,因此无论如何您都没有任何编译时类型安全性,因此直接使用反射不会丢失任何东西。

这是一个isEmpty适用于 Java 反射的实现。它是作为一个函数实现的,但是将其更改为包装类的方法应该很简单:

def isEmpty(a: Any): Boolean = {
  try {
    a.getClass.getMethod("isEmpty").invoke(a).asInstanceOf[Boolean]
  } catch {
    case e: java.lang.NoSuchMethodException => false
  }
}

这里有一些例子:

println(isEmpty("hello"))
println(isEmpty(""))
println(isEmpty(List(1,2,3)))
println(isEmpty(Nil))
println(isEmpty(0 to 10))
println(isEmpty(42 until 42))
println(isEmpty(Some(42)))
println(isEmpty(None))
println(isEmpty((x: Int) => x * x))

它以交替方式打印false/ true。如果它得到一个没有方法的对象,它不会抛出任何令人讨厌的异常isEmpty,如上一个示例所示。


编辑

一个更强大的版本是这样的:

def isEmpty(a: Any): Boolean = {
  try {
    val m = a.getClass.getMethod("isEmpty")
    if (m.getParameterCount == 0) {
      m.invoke(a).asInstanceOf[Boolean]
    } else {
      false
    }
  } catch {
    case e: java.lang.NoSuchMethodException => false
    case e: java.lang.ClassCastException => false
  }
}

它防止isEmpty非空的情况,并且它不返回 a Boolean。但是,它仍然不是“完美的”,因为方法可能会isEmpty重载。也许org.apache.commons.lang3.reflect能派上用场。

于 2018-04-04T21:02:53.207 回答