Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How scala generic constraints to nullable types work

I've tried two ways to constrain a generic type parameter to a nullable type, but both seem to have some unexpected problems.

First attempt (using T <: AnyRef):

scala> def testAnyRefConstraint[T <: AnyRef](option:Option[T]):T = {
     | //without the cast, fails with compiler error:
     | //    "found: Null(null) required: T"
     | option getOrElse null.asInstanceOf[T]
     | }
testAnyRefConstraint: [T <: AnyRef](Option[T])T

scala> testAnyRefConstraint(Some(""))
res0: java.lang.String = 

scala> testAnyRefConstraint(Some(0))
<console>:16: error: inferred type arguments [Int] do not conform to method testAnyRefConstraint's type parameter bounds [T <: AnyRef]
       testAnyRefConstraint(Some(0))

This seems to do exactly what I want, but I don't understand why the null needs to be cast to a T.


Second attempt (using T >: Null):

scala> def testNullConstraint[T >: Null](option:Option[T]):T = {
     | option getOrElse null
     | }
testNullConstraint: [T >: Null](Option[T])T

scala> testNullConstraint(Some(""))
res2: java.lang.String = 

scala> testNullConstraint(Some(0))
res3: Any = 0

This doesn't require the cast on null, but it allows AnyVals to be passed and converts the type to any, which is not what I was looking for.

Does anyone have any idea why these two different approaches work the way that they do?

like image 422
Dan Shryock Avatar asked Feb 25 '10 17:02

Dan Shryock


1 Answers

def testAnyRefConstraint[T >: Null <: AnyRef](option:Option[T]):T = {
  option getOrElse null
}

I felt really stupid when I made this error the first time. Just because something extends AnyRef doesn't mean it must be nullable. For instance, Nothing is a subtype of AnyRef, and it is not nullable.

The other way around is similar, because Any is a supertype of Null, and any Int is also an Any.

like image 147
Daniel C. Sobral Avatar answered Nov 01 '22 16:11

Daniel C. Sobral