I. 类
- 类的定义
- 类成员的可见性
- 方法的定义方式
- 构造器
1. 类的定义
class Classname{
var v = 0
val vl = 1
def func(args): return_type = {content} // 无返回Unit
}
class Counter{
var value = 0
def increment(step: Int): Unit = {value += step}
def current(): Int = {value}
}
// 实例化
val myCounter = new Counter
myCounter.value = 5
myCounter.increment(3)
println(myCounter.current) // 调用无参数方法时,可以省略方法后的括号
2. 类的可见性
当没有修饰词时,成员默认为public
私有成员:
- private -> 本类型和嵌套类型可见
- protected -> 本类型和其他继承类型可见
建议编程中不要直接定义(暴露)public字段,而是通过getter/setter函数去操作某些私有变量。
下面两个方法成对出现:
- value = 类似getter方法
- value_= 一个方法名称,类似setter方法
class Counter{
private var privateValue = 0
def value = privateValue
def value_ = (newValue: Int){
if(newValue > 0) privateValue = newValue
}
...
}
val myCounter = new Counter
myCounter.value_=(3) // 可简化为 myCounter.value = 3
println(myCounter.value) // out: 3 其中.value是一个方法
3. 方法的定义方式
def func(args:arg_type): return_type = {...}
- 参数不可使用val/var修饰的
- 没有参数时,可以省略括号;同时后面定义时必须省略
- 可以使用中缀操作符调用方法
a.+(b) -> a+b - 方法体中只有一条语句时,可省略大括号
- 当无返回值时,可以省略:
Unit=
class Counter{
var value = 0
// def increment(step:Int):Unit = {value += step}
def increment(step:Int){value += step}
def current() = value // value的类型可以自动推断出来,所以不需要声明
}
4. 构造器
类名称(参数列表)
- 类本身作为主构造器 this
- 可以加val/var修饰。同时自动成为类内成员,自动创造读写方法
- 如果没有val/var修饰,作用又是为了传递参数
class Counter(var name: String)
var mycounter = new Counter("Runner")
mycounter.name_=("Timer")
println(mycounter.name) // out: Timer
辅助构造器
一个类:主构造器 + 多个辅助构造器 this
表示当前类对象
- 每个辅助构造器必须调用前一个构造器
```scala
class Counter{
private var value = 0
private var name = “”
private var step = 1
println(“the main constructor”)
def this(name: String){
this.name = name printf(f”the 2nd constructor: $name\n”) } def this(name: String, step: Int){this() // 调用主构造器
this.step = step printf(f”the 3rd constructor: $name\n”) } def increment(step: Int): Unit = {value += step} def current(): Int = {value} }this(name) // 调用前一个辅助构造器
val c1 = new Counter // 主构造器 val c2 = new Counter(“the second Counter”) // 第二个构造器 val c3 = new Counter(“the third Counter”, 3)
---
<a name="JPkhF"></a>
# II. 对象
- 单例对象
- update方法
- apply方法
- unapply方法
<a name="5PSbr"></a>
## 1. 单例对象
- 静态的方法调用,会记住之前的状态,不会重置
- 不需要实例化,可以直接调用静态方法
```scala
object Person{
private var lastId = 0
def newPersonId() = {
lastId += 1
lastId // 方法返回值
}
}
printf(f"The first person id: $Person.newPersonId()")
单例对象分两类
- 伴生对象
- 一个文件中同时出现
class A{}; object A{}
- 互为伴生对象,可以互相访问类内部的变量和方法
- 一个文件中同时出现
- 孤立对象
2. apply**
为什么声明一个数组对象时,不需要new?
- 因为scala会自动帮你调用Array这个类的伴生对象中apply方法,创建一个数组对象
val myStrArr = Array("BigData", "Hadoop", "Spark")
apply调用约定
- 作为一种工厂方法,用于生成类
- 括号传递给类实例/单例对象名称参数时自动调用apply
- scala会在相应的类中查找方法名为apply
- apply参数列表需要与传入的参数一致
- 调用apply方法
class TestApplyClass{
def apply(param: String){
println("apply method called: " + param)
}
}
我们可以将类的构造方法以apply方法的形式,写入伴生对象中。在实例化时,自动调用apply方法,自动生成类。
class Car(name: String){
def info(){
println("Car name is " + name)
}
}
object Car{
def apply(name: String) = new Car(name)
}
// 孤立对象: 定义程序的入口函数
object MyTestApply{
def main(args: Array[String]){
val mycar = Car("BMW") // 调用伴生对象中的apply方法
mycar.info() // out: Car name is BMW
}
}
JAVA使用类过程
- 定义一个类
- new class
- new 一个类,生成一个实例
Scala为了融合面向对象和函数式编程
- 面向对象:对象.方法
- 函数式:函数名称(参数)
def add = (x: Int, y: Int) => x+y //匿名函数
add(4, 5) // 函数调用
add.apply(4, 5) // 对象调用
3. update
- 当对带有括号并包括一到若干参数的对象进行赋值时
- 编译器将调用update方法
- 并将括号里的参数和等号右边的值
- 一起作为update方法的输入参数来执行调用
val myStrArr = new Array[String](3)
myStrArr(0) = "BigData" //调用伴生类Array中update方法执行myStrArr.update(0, "BigData")
4. unapply
给定对象后,提取对象中的变量,解构过程
object Car{
def unapply(c: Car): Option[(String, Int)] = {
Some((c.brand, c.price))
}
}
object TestUnapply{
def main(args: Array[String]){
// 等号右侧apply构造,左侧unapply解构
var Car(carbrand, carprice) = Car("BMW", 800000)
println("brand: " + carbrand + "and carprice" + carprice)
}
}
III. 继承
- 抽象类
- option类
- 扩展类
- Scala的类层次结构
1. 抽象类
一个类中包含没有被实现的成员变量/成员函数,称为抽象类,必须用 abstract
修饰
- 抽象字段必须给定变量类型
abstract class Car(val name: String){
val carBrand: String // 字段没有初始值,是抽象字段
def info() // 抽象方法
def greeting(){
println("Welcome to my car!")
}
}
2. 扩展类 (子类)
extends
关键字表示继承关系;override
覆盖成员
- 父类中已实现的方法,子类覆盖时必须添加
override
- 父类中的抽象方法,子类实现时可不加
override
- 只能重载val字段,不可重载var类型字段 ```scala class BMWCar extends Car{ override val carBrand = “BMW” def info(){ println(“Expensive!”) } override def greeting(){println(“BWM car!”)} }
Object MyCar{ def main(args: Array[string]) val myCar1 = new BMWCar() myCar1.greeting() myCar1.info() }
<a name="ijevc"></a>
## 3. 层次结构
![](https://cdn.nlark.com/yuque/0/2020/png/2799856/1606138930828-fb177d3d-69c0-4bb4-ae9d-99e63a7fcd09.png)
- AnyVal 是值类型,存入寄存器,之后进行运算
- AnyRef 引用类型,new实例化对象后存入堆中,指针指向堆地址 null为子类
<a name="Plul8"></a>
## 4. option类
**实际编程中尽量避免使用Null,改用Option类**
- Option类将返回值封装为Some对象,返回
- 如果没有返回值,返回Null
- 当无法确定返回值,最好将返回值类型设定为Option
```scala
case class Book(val name: String, val price: Double)
val books = Map(
"hadoop" -> Book("Hadoop", 35.5)
"spark" -> Book("Spark", 55.5)
)
$ books.get("hadoop")
res0: Option[Book] = Some(Book(Haddop, 35.5))
$ books.get("Hive")
res1: Option[Book] = None
$ books.get("hadoop").get
res2: Book = Book(Hadoop, 35.5)
$ boos.get("hive").getOrElse(Book("Unknown name", 0))
Book = Book(Unknown name, 0.0)
IV. 特质 trait
Scala 中特有的概念,类似Java中的接口
- 定义抽象方法/方法的具体方法
trait
- 可继承,混入多个特质 -> 实现多重继承
with/extends
```scala trait Flyable{ var maxFlyHeight: Int //抽象字段 def fly()
def breathe(){
} }println("I can breathe.")
class Bird(flyHeight: Int) extends Flyable{ var maxFlyHeight: Int = flyHeight def fly(){ printf(“fly %d”, maxFlyHeight) } }
val b = new Bird(100) b.fly() // fly 100 b.breathe // I can breathe
trait HasLegs{…} class Animal(val category: String){…}
class Bird(flyHeight: Int) extends Animal(“Bird”) with Flyable with HasLegs{ var maxFlyHeight: Int = flyHeight val legs = 2 def fly(){println(‘fly: ‘ + maxFlyHeight)} }
---
<a name="Ic6A7"></a>
# V. 模式匹配
<a name="Nvs26"></a>
## 1. **match-case**
- 可以添加守卫 guard
```scala
import scala.io.StdIn._
println("Please input the score: ")
val grade = readChar()
grade match{
case "A" => println("85-100")
case "B" => println("70-84")
case_ => println("error input!")
}
for(elem <- List(1, 2.2, "Spark")){
val str = elem match{
case i: Int => "int"
case d: Double => "double"
case s: String => "string"
case_ => "unexpected value"
}
println(str)
}
// guard 在每次操作时加入条件判断
for(elem <- List(1,2,3,4)){
elem match{
case_ if(elem%2==0) => println(elem+"is even.")
case_ => println(elem + "is odd.")
}
}
2. case类
- 自动重载许多实用方法
toString/equals/hashcode
- 自动生成一个伴生对象 apply工厂方法自动生成实例,不需要new;还有unapply方法 ```scala case class Car(brand: String, price: Int)
// 上一句case类自动生成下面伴生对象 object Car{ def apply(brand: String, price: Int) = new Car(brand, price) def unapply(c: Car): Option[(String, Int)] = Some((c.brand, c.price)) }
// 用于模式匹配 case class Car(brand: String, price: Int) val myBYD = Car(“BYD”, 89000) val myBMW = Car(“BMW”, 1200000) for(car <- List(myBYD, myBMW)){ car match{ case Car(“BYD”, 89000) => println(“BYD”) //apply case Car(brand, price) => println(“Brand: “ + brand) //unapply } }
---
<a name="yrrT5"></a>
# VI. 包
解决程序中的命名冲突
- 通配符:下划线`_`
- scala隐式添加 `java.lang._` `scala._` `Predef._`
```scala
# 嵌套定义
package xmu{
package autodepartment{
class ControlCourse{
...
}
}
package csdepartment{
class OSCourse{
val cc = new autodepartment.ControlCourse
}
}
}
# 导入使用
import xmu.autodepartment.ControlCourse
class MyClass{
var myos = new ControlCourse
}