目录
快速上手使用版
定义和描述
基于JVM的语言,支持面向对象、面向函数,支持JVM和JavaScript
官网
Spark基于scala进行开发,Flink也提供了关于scala相应的API
idea
idea可能无法正常添加scala的框架(添加框架支持这个方法确实不行)
此处提供另一种办法
先安装scala插件
项目结构---->>库---->>添加scala的sdk
基础
关键字
关键字 | 描述 | 示例 |
---|---|---|
package | 定义一个包 | package com.example.myapp |
import | 导入其他包或对象中的成员 | import scala.collection.mutable.ListBuffer |
class | 定义一个类 | class Person(val name: String, val age: Int) |
object | 定义一个单例对象 | object MainApp extends App { println("Hello, World!") } |
trait | 定义一个特质 | trait Logger { def log(msg: String): Unit } |
extends | 用于类的继承 | class Student(name: String, age: Int) extends Person(name, age) |
with | 用于混入特质或多个特质 | class Admin extends Person("Admin", 0) with Logger |
type | 定义类型别名或类型成员 | type Name = String |
for | 用于循环和推导式 | for (i <- 1 to 5) println(i) |
private | 定义一个私有成员 | class MyClass { private val secret = "secret" } |
protected | 定义一个受保护成员 | class Parent { protected val protectedVal = 1 } |
abstract | 定义一个抽象类或抽象方法 | abstract class Animal { def makeSound: Unit } |
sealed | 限定类的继承 | sealed trait Shape |
final | 禁止类或方法被重写或继承 | final class Constants { val Pi = 3.14 } |
implicit | 定义一个隐式值或隐式转换 | implicit val defaultTimeout: Int = 5000 |
lazy | 定义一个惰性变量 | lazy val lazyVal = { println("Computing..."); 42 } |
override | 重写父类或特质中的方法 | class Dog extends Animal { override def makeSound = println("Woof") } |
try | 捕获异常的代码块 | try { val x = 1 / 0 } catch { case _: ArithmeticException => println("Division by zero!") } |
catch | 捕获异常并处理 | 同上 |
finally | 无论是否发生异常,都会执行的代码块 | try { ... } finally { println("Cleanup!") } |
throw | 抛出一个异常 | throw new IllegalArgumentException("Invalid argument!") |
if | 条件语句 | val x = if (a > b) a else b |
else | 与if语句结合使用,表示其他情况 | 同上 |
match | 模式匹配语句 | val msg = num match { case 1 => "One" case _ => "Other" } |
case | 在match语句中使用,表示一个匹配项 | 同上 |
do | 与while结合使用,表示循环体 | do { println("Looping...") } while (condition) |
while | 条件循环 | while (condition) { println("Looping...") } |
return | 从方法中返回值 | def add(a: Int, b: Int): Int = { return a + b } |
yield | 在推导式(如for-comprehension)中产生一个值 | val squares = for (i <- 1 to 5) yield i * i |
def | 定义一个方法 | def greet(name: String): String = s"Hello, $name!" |
val | 定义一个不可变变量 | val x = 10 |
var | 定义一个可变变量 | var y = 20; y = 30 |
this | 指向当前对象或类的实例 | class MyClass { def printThis = println(this) } |
super | 指向父类或特质的实例 | class Child extends Parent { def callParentMethod = super.parentMethod } |
new | 创建对象或类的实例 | val person = new Person("John", 30) |
true | 布尔值真 | val isTrue = true |
false | 布尔值假 | val isFalse = false |
null | 表示空值或不存在的对象 | val emptyString: String = null |
变量、常量
变量、常量初始化时,必须定义其值
scala
object VariableExample {
def main(args: Array[String]): Unit = {
// 定义不可变变量
val name: String = "Alice"
// 或者省略类型注解,因为编译器可以推断出它是String类型
val age = 30
// 打印变量的值
println(s"Name: $name, Age: $age")
// 定义可变变量
var count: Int = 0
// 修改可变变量的值
count = count + 1
// 打印修改后的值
println(s"Count: $count")
// 尝试修改不可变变量会导致编译错误
// name = "Bob" // 这行代码会导致编译错误,因为name是不可变的
// var a //错误,必须定义值
}
}
输出
java的System.out.println也可以,因为允许混编
scala
object PrintExample {
def main(args: Array[String]): Unit = {
// 使用println打印,并自动换行
println("Hello, World!")
// 使用print打印,不换行
print("Hello, ")
print("Scala!")
// 你也可以打印变量和表达式的值
val name = "Alice"
val age = 30
println(s"Name: $name, Age: $age") // 使用字符串插值
// 打印数组或集合
val numbers = Array(1, 2, 3, 4, 5)
println(numbers.mkString(", ")) // 使用mkString方法将数组转换为字符串
}
}
数据类型
数据类型 | 描述 | 示例 |
---|---|---|
Byte | 8位有符号补码整数。数值区间为 -128 到 127 | val byteVal: Byte = 100 |
Short | 16位有符号补码整数。数值区间为 -32768 到 32767 | val shortVal: Short = 20000 |
Int | 32位有符号补码整数。数值区间为 -2147483648 到 2147483647 | val intVal: Int = -1000000 |
Long | 64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 | val longVal: Long = 9223372036854775807L |
Float | 32位, IEEE 754 标准的单精度浮点数 | val floatVal: Float = 3.14f |
Double | 64位 IEEE 754 标准的双精度浮点数 | val doubleVal: Double = 3.141592653589793 |
Char | 16位无符号Unicode字符, 区间值为 U+0000 到 U+FFFF | val charVal: Char = 'A' |
Boolean | true或false | val booleanVal: Boolean = true |
Unit | 表示无值,和Java语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。 | def printHello(): Unit = println("Hello, World!") |
String | 字符序列 | val stringVal: String = "Hello, Scala!" |
Null | null 或空引用(在Scala中应尽量避免使用null,更推荐使用Option类型来处理可能的空值) | var nullableVal: String = null // 尽量避免这样做 |
Nothing | Nothing类型在Scala的类层级的最底端;它是任何其他类型的子类型。通常用于表示从不正常终止的函数(如抛出异常的函数)。 | def error(): Nothing = throw new Exception("An error occurred") |
Any | Any是所有其他类的超类 | val anyVal: Any = "This can be any type" |
AnyRef | AnyRef类是Scala里所有引用类(reference class)的基类(相当于Java中的Object) | val anyRefVal: AnyRef = new Object() |
AnyVal | AnyVal类是Scala里所有值类(如Int, Double等)的基类(注意:AnyVal本身不是一个具体的类型,而是一个特性标记,用于优化值类型的处理) | // AnyVal是一个抽象概念,不直接用于定义变量 |
注意
- 在Scala中,对于数值类型(如Byte, Short, Int, Long),当值超过其表示范围时,会发生类型溢出。
- 对于Float和Double类型的浮点数,由于精度限制,某些小数可能无法精确表示。
- 在Scala中,应尽量避免使用null值,因为null值可能导致运行时错误(如NullPointerException)。相反,Scala提供了Option类型来处理可能的空值情况。
- Nothing类型通常用于表示从不返回正常结果的函数(如总是抛出异常的函数)。在实际编程中,你很少需要直接声明Nothing类型的变量或返回值。
- Any和AnyRef是Scala类型系统的顶层类型。Any是所有类型的超类,包括值类型和引用类型;而AnyRef是所有引用类型的超类(不包括值类型)。
类型转换
显示类型转换
scala
object ExplicitTypeConversion {
def main(args: Array[String]): Unit = {
// 将Double转换为Int,注意这可能会导致精度丢失
val doubleValue: Double = 123.45
val intValue: Int = doubleValue.toInt // 显式类型转换
println(s"Double to Int: $intValue") // 输出: Double to Int: 123
// 将String转换为Int
val stringValue: String = "456"
val intValueFromString: Int = stringValue.toInt // 这要求字符串内容是一个有效的整数
println(s"String to Int: $intValueFromString") // 输出: String to Int: 456
// 将Any类型转换为具体类型(假设你知道它的实际类型)
val anyValue: Any = "789"
val stringFromAny: String = anyValue.asInstanceOf[String] // 显式类型转换,要求实际类型匹配
println(s"Any to String: $stringFromAny") // 输出: Any to String: 789
}
}
隐式类型转换
scala
object ImplicitTypeConversion {
def main(args: Array[String]): Unit = {
// 隐式地将Byte转换为Int
val byteValue: Byte = 10
val sum: Int = byteValue + 20 // 在这里,byteValue会被隐式地提升为Int类型
println(s"Byte to Int (implicit): $sum") // 输出: Byte to Int (implicit): 30
// 使用Scala的RichInt、RichDouble等隐式转换
val intValue: Int = 10
val bigIntValue: BigInt = intValue // RichInt提供了到BigInt的隐式转换
println(s"Int to BigInt (implicit): $bigIntValue") // 输出: Int to BigInt (implicit): 10
}
}
函数式编程
函数和方法的区别
-
Scala 中存在方法与函数两个不同的概念,二者在语义上的区别很小。scala 方法是类的一部分 ,而函数是一个对象 ,可以赋值给一个变量,也可以作为方法或其它函数的参数。换句话来说在类中定义的即是方法。scala 中的方法跟 Java 的类似,方法是组成类的一部分。scala 中的函数则是一个完整的对象。
-
Scala中的方法和函数从语法概念上来讲,一般不好区分,所以简单的理解就是:方法也是函数。只不过类中声明的函数称之为方法,其他场合声明的就是函数了。类中的方法是有重载和重写 的。而函数没有重载和重写的概念 ,但是函数可以嵌套声明使用,方法不行。
特性 | 方法(Method) | 函数(Function) |
---|---|---|
定义位置 | 类的一部分 | 独立的对象,可以赋值给变量,作为参数传递 |
语义区别 | 在类中定义的即是方法 | 在类外部或其他场合声明的称为函数 |
与Java的关系 | 与Java的方法类似 | 是Scala特有的完整对象 |
重载与重写 | 支持重载和重写 | 不支持重载和重写 |
嵌套声明 | 不能嵌套声明使用 | 可以嵌套声明使用 |
定义示例
有参+带返回值
冒号后面定义的是String表示返回一个String类型的值,如何返回则是在整个函数体的末尾
以下示例代码结果是"wunaiieq"而不是"hello world"
scala
package com.wunaiieq
object Example {
def main(args: Array[String]): Unit = {
/**@param arg 定义的参数,String类型
* @return 冒号后面定义的是String表示返回一个String类型的值,如何返回则是在整个函数体的末尾
* */
def f1(arg: String): String = {
arg + " world"
"wunaiieq"
}
println(f1("hello"))
}
}
有参+没有返回值
scala
package com.wunaiieq
object VariableExample {
def main(args: Array[String]): Unit = {
/**@param arg 定义的参数,String类型
* @return 冒号后面定义的是Unit表示没有返回值
* */
def f1(arg: String): Unit = {
arg + " world"
"wunaiieq"
}
}
}
注意点
-
函数定义语法 用def来定义
-
可以定义传入的参数,要指定传入参数的类型
-
函数可以写返回值的类型也可以不写,会自动推断,有时候不能省略,必须写,比如在递归函数中或者函数的返回值是函数类型的时候。
scala
def funcD3(name:String,score:Double){
//....
//return s"有参数有返回值,Hello ${name},your score is ${score}"
s"有参数有返回值,Hello ${name},your score is ${score}"
}
println(funcD3("diaosi",100))
输出结果为:()
-
scala中函数有返回值时,可以写return,也可以不写return,会把函数中最后一行当做结果返回。当写return时,必须要写函数的返回值。
-
如果函数体可以一行搞定,可以将函数定义时的{}省略不写
-
传递给函数的参数可以在函数中使用,并且scala规定函数的传过来的参数为val的,不是var的。
-
如果去掉函数体前面的等号,那么这个方法返回类型必定是Unit的。这种说法无论方法体里面什么逻辑都成立,scala可以把任意类型转换为Unit.假设,里面的逻辑最后返回了一个String,那么这个返回值会被转换成Unit,并且值会被丢弃。
面向对象
object和class的区别
特性 | object |
class |
---|---|---|
实例化 | 单例,只能有一个实例 | 可以有多个实例 |
实例创建 | 实例在对象定义时自动创建,不能通过new 关键字实例化 |
使用new 关键字来实例化 |
继承 | 不能被继承 | 可以被其他类继承 |
构造函数 | 没有构造函数的概念 | 可以有构造函数,用于初始化实例 |
用途 | 通常用于定义包含main 方法的单例对象,作为程序的入口点 |
用于定义可以实例化的类型,具有状态和行为 |
示例 | object MySingleton { ... } |
class MyClass { ... } |
对象的属性
属性类型 | 描述 | 权限修饰符 | 访问级别 |
---|---|---|---|
val | 不可变属性,一旦赋值后不能改变 | private | 仅类内部可见 |
protected | 类及其子类可见 | ||
public | 所有地方可见(默认) | ||
var | 可变属性,可以在赋值后改变 | private | 仅类内部可见 |
protected | 类及其子类可见 | ||
public | 所有地方可见(默认) | ||
隐式属性(通过getter/setter) | 通过方法定义的属性,Scala鼓励使用这种方式进行属性封装 | private[包名] | 仅在指定包内可见 |
protected[包名] | 在指定包内的类及其子类可见 | ||
无修饰符(默认public) | 所有地方可见 |
注意:
- 在Scala中,
val
用于定义不可变属性,而var
用于定义可变属性。 - 默认情况下,Scala的属性是
public
的,但可以通过添加适当的访问修饰符来限制其访问级别。注意,Scala中没有public关键字 - Scala还提供了包级别的访问修饰符,如
private[包名]
和protected[包名]
,这些修饰符允许你更精细地控制属性的可见性。 - 隐式属性通常是通过getter和setter方法定义的,这是Scala中封装属性的推荐方式。尽管Scala没有显式的语法来定义这些方法,但你可以通过定义带有相同名称的
def
方法来隐式地创建它们。
示例
注意,Scala中没有public关键字
scala
package com.wunaiieq
object AttributeAccessExample {
// 定义一个类,包含不同访问权限的属性
class Person(
private val id: Int, // 私有不可变属性
private[this] var name: String, // 当前对象私有可变属性(注意:private[this]是Scala特有的,限制为当前对象私有)
protected var age: Int, // 受保护可变属性,子类可访问
var email: String // 公共可变属性
) {
// 提供一个公共的getter方法来访问私有属性id
def getId: Int = id
// 提供一个公共的getter和setter方法来访问当前对象私有属性name
def getName: String = name
def setName(newName: String): Unit = {
name = newName
}
// 由于age是受保护的,它可以在子类中被访问和修改
// 这里我们不需要额外的getter和setter方法,除非我们想在类外部以某种方式限制访问
// email已经是公共的,所以我们可以直接访问和修改它
// 为了演示,我们添加一个方法,该方法会根据年龄打印一条消息
def greet(): Unit = {
println(s"Hello, my name is $name and I am $age years old. You can reach me at $email.")
}
}
// 定义一个子类,继承自Person类
class Employee(id: Int, name: String, age: Int, email: String, val jobId: String) extends Person(id, name, age, email) {
// 由于age是受保护的,我们可以在这里访问它
def promote(): Unit = {
var newAge = age.+(1) // 假设晋升会增加年龄(这只是一个示例,实际情况可能不同)
println(s"Employee $name has been promoted. New age: $newAge")
}
}
// 主函数,用于演示如何创建Person和Employee对象,并访问它们的属性
def main(args: Array[String]): Unit = {
// 创建一个Person对象
val person = new Person(1, "Alice", 30, "alice@example.com")
// 由于id是私有的,我们需要通过getId方法来访问它
println(s"Person ID: ${person.getId}")
// 修改name和email属性
person.setName("Alice Smith")
person.email = "alice.smith@example.com"
// 调用greet方法
person.greet()
// 创建一个Employee对象
val employee = new Employee(2, "Bob", 25, "bob@example.com", "ENG001")
// 调用promote方法,这将增加员工的年龄
employee.promote()
// 由于Employee继承自Person,我们可以调用greet方法
employee.greet()
// 注意:我们不能直接访问employee的private[this] name属性,因为它在当前Employee对象之外是不可见的
// 同样,我们也不能直接访问person的受保护属性age,除非我们有一个Person类型的引用并且是在Person类或其子类的上下文中
}
}