我只創建了一個版本爲|||
版本。這是爲了展示這個概念。代碼可能會改進,我衝了一下。
// Create a type that does the conversion, C is the resulting type
trait Converter[A, B, C] {
def convert(a: A, b: => B)(implicit bool: BooleanConverter[A]): C
}
trait LowerPriorityConverter {
// We can convert any type as long as we know how to convert A to a Boolean
// The resulting type should be a supertype of both A and B
implicit def anyConverter[A <: C, B <: C, C] = new Converter[A, B, C] {
def convert(a: A, b: => B)(implicit bool: BooleanConverter[A]) = if (a) a else b
}
// We can go more specific if we can find a view from B to A
implicit def aViewConverter[B <% A, A] = anyConverter[A, A, A]
}
object Converter extends LowerPriorityConverter {
// For Doubles, Floats and Ints we have a specialized conversion as long as the
// second type is a Numeric
implicit def doubleConverter[A <: Double: Numeric, B: Numeric] =
new Converter[A, B, Double] {
def convert(a: A, b: => B)(implicit bool: BooleanConverter[A]) =
if (a) a else implicitly[Numeric[B]].toDouble(b)
}
implicit def floatConverter[A <: Float: Numeric, B: Numeric] =
new Converter[A, B, Float] {
def convert(a: A, b: => B)(implicit bool: BooleanConverter[A]) =
if (a) a else implicitly[Numeric[B]].toFloat(b)
}
implicit def intConverter[A <: Int: Numeric, B: Numeric] =
new Converter[A, B, Int] {
def convert(a: A, b: => B)(implicit bool: BooleanConverter[A]) =
if (a) a else implicitly[Numeric[B]].toInt(b)
}
}
// We have created a typeclass for the boolean converters as well,
// this allows us to use more generic types for the converters
trait BooleanConverter[A] extends (A => Boolean)
trait LowerPriorityBooleanConverter {
implicit def any2bool = new BooleanConverter[AnyRef] {
def apply(s: AnyRef) = s != null
}
}
object BooleanConverter extends LowerPriorityBooleanConverter {
implicit def num2bool[T: Numeric] = new BooleanConverter[T] {
def apply(n: T) = implicitly[Numeric[T]].zero != n
}
// Note that this could catch String as well
implicit def seq2bool[T <% GenTraversableOnce[_]] = new BooleanConverter[T] {
def apply(s: T) = s != null && !s.isEmpty
}
}
// This is similar to the original post
implicit class NonBooleanLogic[A](x: A) {
// Note that we let the implicit converter determine the return type
// of the method
def |||[B, C](y: => B)(
// make sure we have implicits for both a converter and a boolean converter
implicit converter: Converter[A, B, C], bool: BooleanConverter[A]): C =
// do the actual conversion
converter.convert(x, y)
}
有幾個測試結果:
1 ||| 2 //> res0: Int = 1
(null: String) ||| "test" //> res1: String = test
1.0 ||| 2 //> res2: Double = 1.0
1 ||| 2.0 //> res3: Int = 1
List() ||| Seq("test") //> res4: Seq[String] = List(test)
1f ||| 2.0 //> res5: Float = 1.0
1f ||| 2f //> res6: Float = 1.0
0f ||| 2.0 //> res7: Float = 2.0
0 ||| 2f //> res8: Int = 2
2.0 ||| 2f //> res9: Double = 2.0
2.0 ||| 3.0 //> res10: Double = 2.0
Seq("test") ||| List() //> res11: Seq[String] = List(test)
"" ||| "test" //> res12: String = test
正如你所看到的,爲了保持我們需要使用特定的模式類型。我從這裏回答了我自己的一個問題:How to define a method for which the returntype is based on types of argument and type parameter in Scala?
這種方法的優點是可以爲特定類型添加特定的轉換器而不會改變原始代碼。
順便說一句,這是更習慣︰'val x = if(someString!= null && someString.size()> 0)someString else「default string」;' – 2013-02-11 22:46:43
它的意思是JavaScript,而不是Scala – fortran 2013-02-11 22:48:21
你的第二個片段可以寫成'val x = Option(someString).filterNot(_。isEmpty).getOrElse(「default string」)'或'val x = if(someString!= null && someString.size()> 0)someString else 「default string」 – 2013-02-11 22:49:20