我有一个这样的用例;
val fieldType = "int" // we know what is the type of field
val value1 = "1" // can be double/int/long/float etc.
val value2 = "2" // can be double/int/long/float etc.
val operator = "=" // can be >, >=, <, <= etc.
def compare(operator, value1, value2): Boolean = {}
找不到一个好的方法来做这件事。有什么方法可以有效地做到这一点吗?
编辑
我刚做了这个,但不确定这是最有效的方法;
private def apply(fieldType: String, operator: String, value1: String, value2: String): Boolean =
operator match {
case ">" =>
compare(fieldType, value1, value2) > 0
case ">=" =>
compare(fieldType, value1, value2) >= 0
case "<" =>
compare(fieldType, value1, value2) < 0
case "<=" =>
compare(fieldType, value1, value2) <= 0
case "==" =>
compare(fieldType, value1, value2) == 0
}
private def compare(fieldType: String, value1: String, value2: String): Int =
fieldType match {
case "string" => value1.compareTo(value2)
case "int" => value1.toInt.compareTo(value2.toInt)
case "long" => value1.toLong.compareTo(value2.toLong)
case "float" => value1.toFloat.compareTo(value2.toFloat)
}
这个解决方案的优点是它是通用的,所以它适用于所有的数字类型。
我使用Scala为每个数字类型提供的隐式排序。我提供了一个通用的比较方法它适用于数值类型。但我仍然必须解析fieldType
找出什么类型我应该比较。
T : Ordering
表示上下文绑定-这避免了在方法中显式使用隐式参数,并让Scala在方法体中需要的地方自动提供Ordering[T]
类型的参数。
implicitly[Ordering[T]
是一个标准库方法,它告诉Scala查找类型Ordering[T]
的隐式定义,调用它,并返回对象,然后使用特性Ordering
中定义的方法进行比较。
这两个想法融合在一起,消除了为方法包含隐式参数的需要,因为我们对显式排序参数不感兴趣:
def compareBy(fieldType: String)(
op: String,
a: String,
b: String
): Boolean = {
def compare[T: Ordering](op: String, a: T, b: T): Boolean =
op match {
case "==" => implicitly[Ordering[T]].eq(a, b)
case "<" => implicitly[Ordering[T]].lt(a, b)
case "<=" => implicitly[Ordering[T]].lteq(a, b)
case ">" => implicitly[Ordering[T]].gt(a, b)
case ">=" => implicitly[Ordering[T]].gteq(a, b)
case _ => throw new UnsupportedOperationException("op not supported!")
}
fieldType match {
case "byte" => compare(op, a.toByte, b.toByte)
case "short" => compare(op, a.toShort, b.toShort)
case "int" => compare(op, a.toInt, b.toInt)
case "long" => compare(op, a.toLong, b.toLong)
case "float" => compare(op, a.toFloat, b.toFloat)
case "double" => compare(op, a.toDouble, b.toDouble)
case _ =>
throw new UnsupportedOperationException("fieldType not supported!")
}
}
val a = "10"
val b = "2"
val op = ">"
val intFieldType = "int"
val compareInts = compareBy(intFieldType) _
println(compareInts(op, a, b)) // true
这个解决方案似乎设计过度了。以下观察结果可以导致一个更紧凑的例子:
我们可以把任何数字当作
Double
,因为将较小的类型Float
转换为Double
或将整型转换为Double
不会损失精度。在我们想要使用整型比较作为浮点字符串字面量给出的两个数字的情况下,我们使用
longValue
放弃精度,然后将它们转换回Double
,以消除对泛型方法的需要。通过先使用toDoubleOption
,在将浮点字符串文本解析为整型时也可以删除NumberFormatException
。def compareBy( fieldType: String )(op: String, a: String, b: String): Boolean = { def compare(op: String, a: Double, b: Double): Boolean = { op match { case "==" => a == b case "<" => a < b case "<=" => a <= b case ">" => a > b case ">=" => a >= b case _ => throw new UnsupportedOperationException("op not supported!") } } val x: Double = a.toDoubleOption.getOrElse( throw new NumberFormatException(s"given $a is not parsable as $fieldType") ) val y: Double = b.toDoubleOption.getOrElse( throw new NumberFormatException(s"given $b is not parsable as $fieldType") ) fieldType match { case "byte" | "short" | "int" | "long" => compare(op, x.longValue.toDouble, y.longValue.toDouble) case "float" | "double" => compare(op, x, y) case _ => throw new UnsupportedOperationException("fieldType not supported!") } } val a = "10.1" val b = "10.2" val op = "==" val intFieldType = "int" val compareInts = compareBy(intFieldType) _ println(compareInts(op, a, b)) // true