Reputation: 648
In response to my question How to develop macro to short-circuit null? someone pointed me to an earlier long thread with many answers, the most compelling of which to was https://stackoverflow.com/a/5569905 . But it doesn't quite work with Scala AnyVal "primitives" like Int:
object TestOp {
class SafeDereference[A](obj: A) {
def ?[B >: Null](function: A => B): B = if (obj == null) null else function(obj)
}
implicit def safeDereference[A](obj: A) = new SafeDereference(obj)
class C {
def getValue: Int = 0
}
def main(args: Array[String]) {
val c = new C
val x:Int = c ? (_.getValue)
}
}
gives a compilation error of:
[error] TestOp.scala:14: type mismatch;
[error] found : Any
[error] required: Int
[error] val x:Int = c ? (_.getValue)
[error] ^
[error] one error found
[error] {file:/home/mmalak/streaming-demo/}default-ae36bd/compile:compile: Compilation failed
A workaround is to replace val x:Int with val x:java.lang.Integer, and that will compile. Is there a way to improve SafeDereference above so that val x:Int is allowed?
Additional information
The following produces the desired output. The question now becomes how to move the typecasts into SafeDereference, and how to handle all the other Scala "primitives" (Boolean etc).
object TestOp {
class SafeDereference[A](obj: A) {
def ?[B >: Null](function: A => B): B = if (obj == null) null else function(obj)
}
implicit def safeDereference[A](obj: A) = new SafeDereference(obj)
class C {
def getValue: Int = 0
}
def main(args: Array[String]) {
val c:C = null
val x = (c ? (_.getValue)).asInstanceOf[java.lang.Integer].asInstanceOf[Int]
println("x="+x)
}
}
outputs, as desired:
x=0
Upvotes: 0
Views: 299
Reputation: 11244
You could do something like this. The Zero
trait allows you to determine the zero value for any object that is not nullable
. In this case I added one for Numeric
types:
object TestOp {
trait Zero[T] {
def apply(): T
}
object Zero {
implicit def zeroNull[B >: Null] =
new Zero[B] { def apply = null }
implicit def zeroNumeric[B: Numeric] =
new Zero[B] { def apply = implicitly[Numeric[B]].zero }
}
implicit class SafeDereference[A](obj: A) {
def ?[B](function: A => B)(implicit zero: Zero[B]): B =
if (obj == null) zero() else function(obj)
}
class C {
def getValue: Int = 0
def getSomething: C = new C
}
def main(args: Array[String]) {
val c = new C
val x = c ? (_.getValue)
val y = c ? (_.getSomething)
}
}
For Boolean
you would add something like this:
implicit def zeroBoolean[B >: Boolean] =
new Zero[B] { def apply = false }
Upvotes: 2