总体介绍
- Scala中所有的数值类型都是对象,包括Byte,Char,Double,Float,Int,Long和Short。这7个数值类型继承自AnyVal特质,Unit和Boolean类被认为是“非数字的值类型”。
这七个内置的数值类型有相同的数据范围,和java中是等价的。
Data type Range Char 16-bit unsigned Unicode character Byte 8-bit signed value Short 16-bit signed value Int 32-bit signed value Long 64-bit signed value Float 32-bit IEEE 754 single precision float Double 64-bit IEEE 754 single precision float
除了这些类型之外,Boolean有true或者false的值。
如果想要知道这些数据范围的精确值,可以通过Scala的REPL中查到。
scala> Short.MinValue res0: Short = −32768 scala> Short.MaxValue res1: Short = 32767 scala> Int.MinValue res2: Int = −2147483648 scala> Float.MinValue res3: Float = −3.4028235E38
- 除了这些基本类型外,理解BigInt和BigDecimal类是有帮助的,以及在scala.math包中封装的方法。这些都包括在本章中。
- 复数和日期:如果需要比标准Scala更强大的math类,可以查看Spire project,里面包括了像有理数、复数、真值等等,或者查看 ScalaLab,提供了可以在Scala使用的科学计算Matlab-like。
Java中Joda Time项目用于处理日期是非常流行,一个叫做nscala-time的项目实现了Scala包裹Joda Time,可以让你用Scala方法写日期表达式,包括下面例子:
DateTime.now // returns org.joda.time.DateTime DateTime.now + 2.months DateTime.nextMonth < DateTime.now + 2.months (2.hours + 45.minutes + 10.seconds).millis
2.1 从字符串中解析数字
- 问题:要把字符串转成Scala的任意数值类型。
2.1.1 解决方案
在字符串后面使用to*方法(StringLike特质)
scala> "100".toInt res0: Int = 100 scala> "100".toDouble res1: Double = 100.0 scala> "100".toFloat res2: Float = 100.0 scala> "1".toLong res3: Long = 1 scala> "1".toShort res4: Short = 1 scala> "1".toByte res5: Byte = 1
小心,因为这些方法会抛出Java的NumberFormatException异常:
scala> "foo".toInt java.lang.NumberFormatException: For input string: "foo" at java.lang.NumberFormatException.forInputString(NumberFormatException.java) at java.lang.Integer.parseInt(Integer.java:449) ... more output here ...
BigInt和BigDecimal实例也可以直接从字符串中创建(也可以抛出NumberFormatException异常):
scala> val b = BigInt("1") b: scala.math.BigInt = 1 scala> val b = BigDecimal("3.14159") b: scala.math.BigDecimal = 3.14159
2.1.2 进制处理
如果需要使用其他基于10的基数进行计算,可以发现在Scala的Int类里的toInt方法没有一个让你根据进制处理的方法。解决这个问题,使用java.lang.Integer类里的parseInt方法。
scala> Integer.parseInt("1", 2) res0: Int = 1 scala> Integer.parseInt("10", 2) res1: Int = 2 scala> Integer.parseInt("100", 2) res2: Int = 4 scala> Integer.parseInt("1", 8) res3: Int = 1 scala> Integer.parseInt("10", 8) res4: Int = 8
如果你是隐式转换的粉丝,你可以创建一个隐式类和方法来解决这个问题:
implicit class StringToInt(s: String) { def toInt(radix: Int) = Integer.parseInt(s, radix) }
定义一个隐类并添加到范围里,添加一个toInt方法,以一个基数参数到String类,现在可以调用这个方法代替Integer.parseInt:
scala> implicit class StringToInt(s: String) { | def toInt(radix: Int) = Integer.parseInt(s, radix) | } defined class StringToInt scala> "1".toInt(2) res0: Int = 1 scala> "10".toInt(2) res1: Int = 2 scala> "100".toInt(2) res2: Int = 4 scala> "100".toInt(8) res3: Int = 64 scala> "100".toInt(16) res4: Int = 256
2.1.3 讨论
- 如果使用过Java的字符串转数值类型,就会对NumberFormatException很熟悉,然后Scala没有异常检查,所以可能会想以不同的方式处理这种情况。
首先,没有必须声明在Scala方法可能会抛出一个异常,所以像这样声明一个Scala方法是完全合法的:
// not required to declare "throws NumberFormatException" def toInt(s: String) = s.toInt
- 如果你允许像这样抛出一个异常,你方法的调用者可能需要提前知道这可能发生,考虑给你的方法添加一个Scala文档。
如果你更喜欢给你的方法抛出一个异常,使用@throws注解进行标记:
@throws(classOf[NumberFormatException]) def toInt(s: String) = s.toInt
- 如果这个方法在Java代码中被调用是需要进行@throws注解标注的,详细看第17.2章。
然而,在Scala中这种情况通常使用Option/Some/None模式进行处理,详细描述见20.6章,使用这种方式如下定义方法:
def toInt(s: String):Option[Int] = { try { Some(s.toInt) } catch { case e: NumberFormatException => None } }
现在可以根据不同需求用不同的方法调用toInt,一种使用getOrElse:
println(toInt("1").getOrElse(0)) // 1 println(toInt("a").getOrElse(0)) // 0 // assign the result to x val x = toInt(aString).getOrElse(0)
另一个方法就是使用匹配表达式,使用匹配表达式打印toInt结果:
toInt(aString) match { case Some(n) => println(n) case None => println("Boom! That wasn't a number.") }
使用匹配表达式分配结果给一个变量:
val result = toInt(aString) match { case Some(x) => x case None => 0 // however you want to handle this }
- 如果这些例子还是没有让你很好的使用 Option/Some/None方法,可以查看第10章和第11章,这个模式在处理集合的时候会非常有用和方便。
2.1.4 Option的选择
- 如果你喜欢Option/Some/None思想,但是需要访问异常信息,有几个额外的可能性:
- Try, Success, and Failure (2.10描述)
- Either, Left, and Right
- 查看更多
2.2 数值类型间转换(强转)
- 问题:需要从一个数值类型转换成另一个数值类型,比如Int转成Double。
2.2.1 解决方案
除了在Java中使用的“cast”方法,在所有数值类型上都可使用to*方法,演示如下:
scala> val b = a.to[Tab] toByte toChar toDouble toFloat toInt toLong toShort toString scala> 19.45.toInt res0: Int = 19 scala> 19.toFloat res1: Float = 19.0 scala> 19.toDouble res2: Double = 19.0 scala> 19.toLong res3: Long = 19 scala> val b = a.toFloat b: Float = 1945.0
讨论:
在Java中通过强转从一个数值类型转换到另一个数值类型:
int a = (int) 100.00;
- 但是在Scala中使用to*方法即可。
如果你想避免潜在的转换错误,可以在转换之前尝试使用相关的isValid方法测试类型是否可以被转换。举例,Double对象有一个isValidInt和isValidShort方法:
scala> val a = 1000L a: Long = 1000 scala> a.isValidByte res0: Boolean = false scala> a.isValidShort res1: Boolean = true
- 查看更多
2.3 重写默认数值类型
- 问题:Scala会自动给数值分配类型,当你创建一个数值字段时需要重写默认类型。
2.3.1 解决方案:
如果你把1赋值给一个变量,Scala会分配Int类型:
scala> val a = 1 a: Int = 1
下面的例子显示一种重写简单数值类型的方法:
scala> val a = 1d a: Double = 1.0 scala> val a = 1f a: Float = 1.0 scala> val a = 1000L a: Long = 1000
另一个方法是用一个类型进行注解:
scala> val a = 0: Byte a: Byte = 0 scala> val a = 0: Int a: Int = 0 scala> val a = 0: Short a: Short = 0 scala> val a = 0: Double a: Double = 0.0 scala> val a = 0: Float a: Float = 0.0
冒号后面的间距不重要,如果首选的话使用下面的格式:
val a = 0:Byte
根据 Scala Style Guide,这些例子显示了首选的注解类型,但是个人而言,当分配类型给变量时我更喜欢下面的语法,在变量名后面指定类型:
scala> val a:Byte = 0 a: Byte = 0 scala> val a:Int = 0 a: Int = 0
可以在数字之前添加0x或者0X创建十六进制数值,然后可以把他们当做整形或者长整形存储:
scala> val a = 0x20 a: Int = 32 // if you want to store the value as a Long scala> val a = 0x20L a: Long = 32
在一些罕见的情况下,可能需要利用类型归属,Stack Overflow显示了一个String向上转型到Object的例子:
scala> val s = "Dave" s: String = Dave scala> val p = s: Object p: Object = Dave
- 这个技术和这章节很像,向上转型常见于类型归属。官方文档描述如下:
- 归属是基本的向上转型,编译执行时的类型检查,使用并不常见。但是可能在某些情况发生,最常见的情况是使用一个单一序列参数调用一个可变方法。
2.3.2 讨论
创建对象实例的时候很有用,大体语法如下:
// general case var [name]:[Type] = [initial value] // example var a:Short = 0
当需要在一个类中初始化数值变量字段时,这种形式将非常有用。
class Foo { var a: Short = 0 // specify a default value var b: Short = _ // defaults to 0 }
当分配一个初始化值时可以使用下划线作为占位符,当创建类变量时起作用,但是在其他地方不起作用,比如在一个方法里不起作用。对于数值类型这不是问题,你可以指定值为0,但是在其他类型中,你可以在方法中用下面的方法:
var name = null.asInstanceOf[String]
- 更好的方式是使用 Option/Some/None模式,它有助于消除你代码中的空值,这是一个非常好的东西。你可以在Scala最好的库和框架中看到这个模式,比如Play框架。最好的例子看12.4章。
- 更多讨论这个话题的信息看20.5章和20.6章。
- 查看更多
The Scala Style Guide
What is the purpose of type ascriptions in Scala?