跳转到内容

Scala/基本类型

来自维基教科书,一个开放的世界,开放的书籍

静态类型

[编辑 | 编辑源代码]

Scala 是一种静态类型的语言。这意味着当 Scala 程序被编译时,编译器会验证对对象的运算是否根据它们的类型有效,如果不是,就会给出关于类型错误的信息。一个简单的例子,由于类型错误而被拒绝的程序

println(" " * 4.5) //ERROR: Required "Int" but found "Double".

字符串方法 "*" 未为实数定义,因此当编译器编译程序时,它无法找到任何版本在字符串上使用实数作为参数的 "*" 方法,因此它输出一条错误信息。错误信息取决于编译器及其版本,但通常包括位置、特定类型错误和其他有用信息。

通过给出整数值来修复程序,我们得到以下程序

println(" " * 4) //Prints "    ".

由于字符串方法 "*" 为整数定义,因此编译器会验证类型是否有效,并接受程序。

类型注解和类型推断

[编辑 | 编辑源代码]

可以使用类型注解来指示值、变量和表达式的类型

val someInteger = 3:Int
val someReal = 4:Double
//val someString = 5:String //ERROR: Required "String" but found "Int".

在第一行中,我们声明一个名为 "someInteger" 的值,它被赋予表达式 "3:Int"。冒号后跟着 "Int" 是一个类型注解,指定 3 是 "Int" 类型。编译器在类型检查期间会验证这是否有效。在第二行中,我们声明一个名为 "someReal" 的值,它被赋予表达式 "4:Double"。虽然 "4" 通常被解释为 "Int",但类型注解意味着它被解释为 "Double"。在第三行中,我们声明一个名为 "someString" 的值,它被赋予表达式 "5:String"。但是,"5" 不能被解释为 "String",类型检查会失败,并从编译器收到错误信息。

在上面的例子中,"3" 的类型注解不是严格要求的。类型注解通常是可选的,可以放置在多个地方

val someInt = 4 + 6
val someInt2:Int = 4 + 6
val someInt3 = (4 + 6):Int
val someInt4:Int = (4 + 6):Int
val someInt5:Int = (4 + 6:Int):Int

类型注解通常不需要的原因是 Scala 支持类型推断,这意味着它通常可以根据不同的元素推断出类型。类型注解可以作为类型推断器的指南,例如,当它难以推断类型时,或者当它推断出错误的类型时。

需要类型注解的例子包括函数定义的参数和递归函数的返回值类型。

数值类型

[编辑 | 编辑源代码]

布尔型

[编辑 | 编辑源代码]

"Boolean" 只有两个可能的值:true 和 false。关键字 "true" 和 "false" 是 "Boolean" 的字面量

val thisIsTrue = true
val thisIsFalse = false

布尔值上有几个运算符。最常见的单目运算符是补码或否定,在 Scala 中由 "!" 方法实现

println(!true) //Prints "false".
println(!false) //Prints "true".

两个最常见的二元运算符是合取,或 "and",和析取,或 "inclusive or"。在 Scala 中,合取和析取分别由 "&&" 和 "||" 方法实现

println(true && true) //Prints "true".
println(true && false) //Prints "false".
println(false && true) //Prints "false".
println(false && false) //Prints "false".
println(true || true) //Prints "true".
println(true || false) //Prints "true".
println(false || true) //Prints "true".
println(false || false) //Prints "false".

其他二元运算符包括等于(由 "==" 实现)、不等于(由 "!=" 实现)和异或(由 "^" 实现)。

布尔型通常用于决策。一个简单的例子是 if-then-else 表达式,它使用 Boolean 类型的表达式来决定哪个分支需要计算

val result = if (true && (false || true) && !false) 3 else 5
println(result) //Prints "3".

整数型

[编辑 | 编辑源代码]

整数类型描述了离散的数字,即它们没有任何分数。不同的整数类型在它们可以表示的数字范围上有所不同。它们包括 "Byte"、"Short"、"Int"、"Long"、"BigInt" 和 "Char"。下表描述了每种类型可以表示的离散数字的范围

类型 最小数字 最大数字
Byte -27 27 - 1
Short -215 215 - 1
Int -231 231 - 1
Long -263 263 - 1
BigInt -无穷大 无穷大
Char 0 216 - 1

Byte、Short、Int 和 Long

[编辑 | 编辑源代码]

"Byte"、"Short"、"Int" 和 "Long" 用于表示数字或数据。它们支持简单的算术运算符,例如否定、加法、减法、乘法、除法和模运算

println(-2) //Prints "-2".
println(2 + 5) //Prints "7".
println(2 - 5) //Prints "-3".
println(2 * 5) //Prints "10".
println(2 / 5) //Prints "0".
println(2 % 5) //Prints "2".
println(-(2:Byte)) //Prints "-2".
println((2:Short) + (5:Int)) //Prints "7".
println((2:Byte) - (5:Long)) //Prints "-3".

注意,涉及 "Byte"、"Short" 和 "Int" 但不涉及 "Long" 的算术运算将转换为 "Int",而涉及 "Long" 和任何其他类型的运算将转换为 "Long"。模运算可以给出负值,除法和模运算的设计使得等式 "b * (a/b) + (a%b) == a" 通常成立,即使对于负 "a" 和 "b" 也成立。除法或模运算的第二个参数不能为 0

//println(3 / 0) //ERROR: Throws an arithmetic exception.
//println(3 % 0) //ERROR: Throws an arithmetic exception.

也支持关系运算符,例如小于、小于或等于、等于和不等于

println(3 < 5) //Prints "true".
println(3 <= 5) //Prints "true".
println(3 == 5) //Prints "false".
println(3 != 5) //Prints "true".

其他关系运算符包括大于和大于或等于,它们分别由 ">" 和 ">=" 实现。

位运算符包括 "~"、"&"、"|" 和 "^",位移运算符包括 "<<"、">>" 和 ">>>"。

字面量可以通过写出数字来声明。替代表示法包括八进制和十六进制

println(10) //Prints "10".
println(010) //Prints "8".
println(0x10) //Prints "16".
println(0X10) //Prints "16".

在第一行中,打印 "10"。在第二行中,我们通过在数字前加上 "0" 来写八进制数 "10",并打印 "8",这是相应的十进制数。在第三行中,我们通过在数字前加上 "0x" 来写十六进制数 "10",并打印 "16",这是相应的十六进制数。第四行与第三行类似,只是使用 "0X" 而不是 "0x" 来进行前缀。请注意,数字总是以十进制表示法打印。

除了前缀 "0x" 或 "0X" 之外,十六进制字面量还包含普通数字以及字母 "a" 到 "f"。小写和大写之间没有区别

println(0xff) //Prints "255".
println(0xFF) //Prints "255".
println(0xA0) //Prints "160".
println(0x10aB) //Prints "4267".

"Long" 字面量通过在数字后面添加 "l" 或 "L" 来表示

println(100l) //Prints "100".
println(077L) //Prints "63".
println(0xFFL) //Prints "255".
//println(2147483648) //ERROR: integer number is too large.
println(2147483648L) //Prints "2147483648".

整数溢出和下溢是当在某个整数表达式中,计算出的数字对于整数类型来说太大或太小而无法容纳时发生的

println(2147483647) //Prints "2147483647".
println(2147483647 + 1) //Prints "-2147483648".
println(-2147483648 - 1) //Prints "2147483647".

在第一行中,打印了 "Int" 的最大数字。在第二行中,打印了 "Int" 的最大数字加 1,错误地给出了 "Int" 的最小数字。在第三行中,打印了 "Int" 的最小数字减 1,错误地给出了 "Int" 的最大数字。下溢和溢出通常不会引起异常,但会导致错误的结果。

避免溢出和下溢的常规做法是确保使用的整数类型始终足够大以容纳表达式。这必须在计算的每一步都完成

val wrongResult:Long = 24 * 60 * 60 * 1000 * 1000
println(wrongResult) //Prints "500654080".
val rightResult:Long = 24L * 60 * 60 * 1000 * 1000
println(rightResult) //Prints "86400000000".

在第一行中,表达式中的整数字面量被解释为 "Int"。因为 "Int" 不足以包含所有中间结果,所以会发生溢出,并给出错误的结果。在错误的结果计算出来后,它被转换为 "Long" 并存储在值 "wrongResult" 中。在第二行中,打印 "wrongResult",给出错误的数字 "500654080"。在第三行中,第一个整数字面量被指示为 "Long" 类型。由于运算符是左结合的,并且涉及 "Int" 和 "Long" 的表达式具有 "Long" 类型,所以中间结果的类型为 "Long",并且足够大以容纳结果数字,而不会发生溢出。结果被存储在值 "rightResult" 中。在第四行中,打印 "rightResult",给出正确的数字 "86400000000"。

BigInt 支持任意精度,这意味着它可以表示所有离散数字,只要有足够的内存可用

//val someNum = 100000000000000000000L //ERROR: integer number is too large.
val someNum = BigInt("100000000000000000000")
println(someNum) //Prints "100000000000000000000".
println(someNum + someNum * 2) //Prints "300000000000000000000".

在第一行,我们尝试使用“Long”字面量声明一个数字,但该数字太大而无法存储在“Long”中,编译器会报错。在第二行,我们使用字符串声明了相同的数字,并将其分配给“someNum”值。在第三行,我们打印“someNum”的值。在第四行,我们对someNum和一个“Int”类型的整数字面量进行基本运算,并打印结果。

“BigInt”的运算和内存使用取决于它所表示的数字的大小,通常比其他整数类型慢。由于它具有任意精度,因此纯粹使用“BigInt”进行的计算永远不会导致整数溢出或下溢。

“Char”首先用于表示字符串中的字符。

待办事项:扩展此部分。

字符串

[编辑 | 编辑源代码]

特殊类型

[编辑 | 编辑源代码]

待办事项:写一些关于“Unit”,“Any”,“Nothing”等内容。也许还有“null”。待办事项:讨论结合律。

华夏公益教科书