Reputation: 11532
If I call this with 3 I get back an Int-typed return as I expect.
def determineKind( a:Int ):Any = {
if( a < 5 )
a.toInt
else if(a<50)
a.toLong
else if(a<100)
a.toFloat
else
a.toDouble
}
If I call this with 3, I get back a Double.
def determineKind2( a:Int ):Any = {
val aResult = { if( a < 5 )
a.toInt // Execution flows here
else if(a<50)
a.toLong
else if(a<100)
a.toFloat
else
println("Oops!") // Never executed--yet I get a Double back!
a.toDouble
}
aResult
}
Why? I don't want the Double. (The real function uses a match/case block, which also seems to convert any of these numerical types to Double.)
Something about wrapping things in a code block triggers the unwanted type change to Double.
Why is that?
Upvotes: 3
Views: 85
Reputation: 1
Greg, this block below returns value of type Double because the last thing you do in it is calling toDouble method on a.
val aResult = {
if( a < 5 )
a.toInt // Execution flows here
else if(a<50)
a.toLong
else if(a<100)
a.toFloat
else
println("Oops!") // Never executed--yet I get a Double back!
a.toDouble // <---- this line is not a part of if else expression
}
That's a good reason why to use curly braces, so you can prevent mistakes like this.
Upvotes: 0
Reputation: 12783
You problem lies in how you defined the if block, effectively, in determineKind2
, a.toDouble
is always assigned to aResult
and the entire result of the if block is discarded.
The illustration of what is happening:
def block = {
if(condition)
v1
else
println(v2)
v2
}
Always returns v2. This happens because the same code using explicit curly braces would be this:
def block = {
if(condition) {
v1
} else {
println(v2)
}
v2
}
Notice how v2 is outside the if block. In case statement block on the other hand:
condition match {
case true => v1
case _ =>
println(v2)
v2
}
The same doesn't happen above, the blocks in case
s are ended by the start of another case in the same scope depth.
As a side note: indentation doesn't have any effect in how blocks are compiled.
Upvotes: 4
Reputation: 67310
This is a tricky situation where numeric widening comes into play. In the first example, you are stating the return type, Any
, and thus there is no need to widen any of the types. In the second case, val aResult =
you are asking Scala to infer the type of the if-expression, and it will avoid to infer Any
and instead decide to widening the individual types numerically to a Double
. Often this widening is what you'd want, e.g. say you write Seq(1, 2.3, 4.5)
- you'd expect that to be a Seq[Double]
and not Seq[Any]
.
If you define the type of aResult
, you ask Scala to accept the unspecific type of Any
:
def determineKind(a: Int): Any = {
val res: Any /* ! */ = if (a < 5)
a.toInt
else if (a < 50)
a.toLong
else if(a < 100)
a.toFloat
else
a.toDouble
res
}
assert(determineKind(3).isInstanceOf[Int])
Numeric widening appears in section 6.26.1 of the Scala Language Specification:
If e has a primitive number type which weakly conforms to the expected type, it is widened to the expected type using one of the numeric conversion methods
toShort
,toChar
,toInt
,toLong
,toFloat
,toDouble
defined here.
Upvotes: 2