Gaurang Shah
Gaurang Shah

Reputation: 12900

scala: why code returns List[Any] and not List[String]?

I am trying to learn the scala, and one thing confuses me is this:

Why following code returns List[Any] and not List[String]

def listToLowerCase(l: List[String]) = l.map(s=>if(s != null) s.toLowerCase) 

is it because when string is null I am not even call lowerCase function? but shouldn't it return nothing?

Upvotes: 1

Views: 64

Answers (2)

In Scala, most control structures are expressions and not statements.
Thus, the if-else has to return a value. Since you do not have an else branch, the compiler inserts this else (). So your code returns sometimes Strings other times Unit and the LUB of those two types is Any.

There are a couple of ways to solve this, one is to ensure the list doesn't have nulls before the map (in general in Scala we never expect a null). Other is to return some default value when the string is null for example and empty string. Another one is to use the Option data type to represent missing values.

Here are some suggestions:

list.filter(_ != null).map(_.toLowerCase)
list.collect { case str if (str != null) => str.toLowerCase }
list.map(str => if (str != null) str.toLowerCase else "")
list.map(str => Option(str).map(_.toLowerCase))
list.map(str => Option(str).map(_.toLowerCase)).getOrElse("")
list.map(str => Option(str).fold(ifEmpty = "")(_.toLowerCase))
list.flatMap(str => Option(str).map(_.toLowerCase))

I would recommend you the last four, as direct manipulation of null in Scala is not recommended. Except if performance is a must and the allocation of Option is too expensive for your use case (that is rarely the case).

Upvotes: 3

Mario Galic
Mario Galic

Reputation: 48410

When else part of conditional expression is not provided

if(s != null) s.toLowerCase

then by default it expands to

if(s != null) s.toLowerCase else ()

where s.toLowerCase types to String whilst () types to Unit, hence the least upper bound of the two is Any. As per SLS

A short form of the conditional expression eliminates the else-part. The conditional expression if (𝑒1) 𝑒2 is evaluated as if it was if (𝑒1) 𝑒2 else ().

Note we could filter out null before lower-casing like so

List[String]("AA", null).flatMap(Option(_)).map(_.toLowerCase)
// res0: List[String] = List(aa)

because Option(null) is None.

Upvotes: 1

Related Questions