How to return null from a generic function in Scala?
Asked Answered
S

4

15

I am writing my own simple javax.sql.DataSource implementation, the only method of it I need to work is getConnection: Connection, but the interface inherits many other methods (which I don't need) from javax.sql.CommonDataSource and java.sql.Wrapper. So, I would like to "implement" those unneeded methods a way they wouldn't actually work but would behave an adequate way when called. For example I implement boolean isWrapperFor(Class<?> iface) as

def isWrapperFor(iface: Class[_]): Boolean = false

and I'd like to implement <T> T unwrap(Class<T> iface) as

def unwrap[T](iface: Class[T]): T = null

But the last doesn't work: the compiler reports type mismatch.

Will it be correct to use null.asInstanceOf[T] or is there a better way? Of course I consider just throwing UnsupportedOperationException instead in this particular case, but IMHO the question can still be interesting.

Sartorial answered 8/4, 2012 at 19:53 Comment(0)
S
26

This is because T could be a non-nullable type. It works when you enforce T to be a nullable type:

def unwrap[T >: Null](iface: Class[T]): T = null

unwrap(classOf[String]) // compiles

unwrap(classOf[Int]) // does not compile, because Int is not nullable
Snappish answered 8/4, 2012 at 20:4 Comment(5)
Didn't new that there are non-nullable types in Scala... I thought Int is nullable unlike Java primitive int...Sartorial
Int is of type AnyVal and value types are not nullable.Snappish
as @oxbow_lakes mentions though, throwing is preferable to returning null in most casesWalrath
This helped me out (trying to write a helper that makes it easier to call a Java library). As a side note do you know why you can't you use an upper bound of AnyRef? I thought Null a subtype of all reference classes?Swearingen
You can use AnyRef, but then you have to cast null to T. Also I think it's just clearer to say this type has to be nullable instead of saying it has to be a reference type.Snappish
N
6

The "correct" solution is to do something which will immediately fail. Like so:

def unwrap[T](iface: Class[T]): T = sys.error("unimplemented")

In scala 2.10, this would have been implemented as:

def unwrap[T](iface: Class[T]): T = ???

Because there is a new method in Predef called ???. This works because an expression of the form throw new Exception has the type Nothing, which is a subtype of any type (it is called bottom in type-theoretical circles).

The reason that this is correct is that it is much better to fail instantly with an error, rather than use a null which may fail later and obfuscate the cause.

Nonjoinder answered 8/4, 2012 at 22:30 Comment(2)
I haven't dealt with isWrapperFor/unwrap in practice but suppose that unwrap is to equal null in case isWrapperFor equals false and implementing isWrapperFor as = null seems the most adequate in case the object can never be a wrapper. Am I not right?Sartorial
def ??? = throw new UnsupportedOperationException // very easy to add in Scala < 2.10Walrath
S
1

As said in comments, this solution doesn't work

If I understood your problem well, you can also assign default values, as explained in details in what does it mean assign "_" to a field in scala?. You can find more infos in 4.2 Variable Declarations and Definitions of the The Scala Language Specification

So you can simply do:

def unwrap[T](iface: Class[T]): T = _

which will set unwrap with null without the type mismatch.

Superannuated answered 8/4, 2012 at 21:9 Comment(1)
That is true, I didn't know this didn't work with functions, which by the way is sad. Thanks for the correctionSuperannuated
B
0

You can use option:

def nullHandledFunctionCall[T]( obj: Option[T], function: T=>T ) = {
    if(obj == null){
      None
    }else{
      obj.map(function)
    }
  }
Barrage answered 26/12, 2023 at 23:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.