1. 类
1.1 类的创建
// 方式一
class A{
// 方法和字段的定义
}
// 方式二
new A
1.2 定义字段和方法
object ClassTest {
def main(args: Array[String]): Unit = {
// Scala在调用无参方法时,是可以省略方法名后面的圆括号的
val c = new Counter // 创建对象
println(c.current()) // 方法调用
c.increment()
}
}
class Counter {
// 私有字段,内内部使用
private var price = 12;
def increment(): Unit = {
price += 1
println("价格为:" + price)
}
def current(): Int = {
price
}
}
private
修饰的字段为私有变量,只能在类内部使用Unit
表示该方法没有返回值,类似于Java
的void
scala
方法返回值不需要写return
,方法最后一个表达式的值就是返回值
简写
class Counter {
// 私有字段,内内部使用
private var price = 12;
// 省略大括号
def increment(): Unit = price + 1
// 省略返回类型
def increment2() {price+1}
// def increment(): Unit = {
// price += 1
// println("价格为:" + price)
// }
//
// def current(): Int = {
// price
// }
}
1.3 编译和执行
三种执行方式
-
将你在
ide
写好的project
在scala
下进行编译,之后通过Main
调用 -
打包整个项目为
jar
,通过scala -classpath
加载后,在scala
中import
进行调用 -
去掉
package
声明,并且将依赖包通过scala -classpath
加载后,再使用:load
加载你的类
参数解释
-
scalac
:scala
的编译器 -
-classpath
:指明外部依赖包 -
-d
:指明 编译后的输出文件 要放到哪里,这里把编译结果放在classes
文件下 -
最后指明需要编译的
scala
文件
终端中编译执行
// 切换 scala 程序所在目录,编译,编译成功生成 ClassTest$.class、ClassTest.class、Counter.class 三个文件
F:\JavaStudy\ScalaStudy\src>scalac ClassTest.scala
// 执行
F:\JavaStudy\ScalaStudy\src>scala ClassTest
12
价格为:13
// 示例
// 编译
scalac -classpath lib/spark-assembly-1.5.1-hadoop2.6.0.jar -d classes src/com/xzj/process/*
// 执行
scala -classpath classes:lib/spark-assembly-1.5.1-hadoop2.6.0.jar com.xzj.process.Main
scala 交互解释器中调用
scala> :load F:\JavaStudy\ScalaStudy\src\ClassTest.scala
Loading F:\JavaStudy\ScalaStudy\src\ClassTest.scala...
defined object ClassTest
defined class Counter
scala> val c = new Counter
c: Counter = Counter@c4ed84
scala> c.current()
res0: Int = 12
scala> println(c.current())
12
scala> c.increment()
价格为:13
1.4 getter和setter
Java
中有时候经常把一些字段定义为private
类型来完成封装,这样外界就无法访问。如果外界访问或者修改该字段的时候,只能通过该字段提供的getter
和setter
方法来实现。但是在Scala
中是没有getter
和setter
一说的,但是可以用 value 和 value_=
来代替:
class CounterTest{
private var privateValue = 0;//私有变量,外界无法直接访问
def value = privateValue;//定义一个方法,方法名为我们想要的字段的名称,代替getter
// value_= 是方法名字
def value_= ( newValue : Int ){
privateValue = newValue
}
}
object MyCounter{
def main(args:Array[String]){
val myCounter = new CounterTest
println(myCounter.value);//调用value方法访问value,相当于getter
myCounter.value = 3 ;//为value设置新值,相当于setter
println(myCounter.value)
}
}
注意:
setter
是用value_=
代替的,下划线和等号中间没有空格,没有空格,没有空格!
参考文章:【scala】getter和setter
1.5 辅助构造器
- 辅助构造器名称为
this
,可以有多个辅助构造器 - 每个辅助构造器必须调用一个一已定义的主或辅助构造器
object MyCounter2 {
def main(args: Array[String]): Unit = {
// 不同的参数调用不同的构造器
val c1 = new Counter // 主构造器
val c2 = new Counter("rose") // 第一个辅助构造器
val c3 = new Counter("lila", 2) // 第二个辅助构造器
c1.info()
c2.info()
c3.info()
}
}
class Counter {
private var value = 0
private var name = "tom"
private var mode = 1
// 第一个辅助构造器
def this(name: String) {
this() // 调用主构造器
this.name = name
}
// 第二个辅助构造器
def this(name: String, mode: Int) {
this(name) // 调用第一个辅助构造器
this.mode = mode
}
def info(): Unit = {
printf("Name is %s, mode is %s\n", name, mode)
}
}
运行结果:
Name is tom, mode is 1
Name is rose, mode is 1
Name is lila, mode is 2
1.6 主构造器
Scala
的主构造器与 Java
不一致,其参数要定义在类名称后面:
object MyCounter2 {
def main(args: Array[String]): Unit = {
// 主构造器
val c4 = new MainCounter("rose", 1)
c4.info()
}
}
// 主构造器
class MainCounter(name: String, mode: Int) {
def info(): Unit = {
printf("name is %s, mode is %s", name, mode)
}
}
运行结果:
name is rose, mode is 1
2. 对象
2.1 单例对象
object SingletonObject {
def main(args: Array[String]): Unit = {
println("单例对象:first: " + TestSingletonObject.newNum())
println("单例对象:second: " + TestSingletonObject.newNum())
println("单例对象:third: " + TestSingletonObject.newNum())
// 普通对象
val c1 = new CommonObject
val c2 = new CommonObject
val c3 = new CommonObject
println("普通对象:" + c1.newNum())
println("普通对象:" + c2.newNum())
println("普通对象:" + c3.newNum())
}
}
// 单例对象
object TestSingletonObject {
private var num = 0
def newNum() = {
num += 1
num
}
}
// 普通对象
class CommonObject {
private var num = 0
def newNum() = {
num += 1
num
}
}
运行结果:
单例对象:first: 1
单例对象:second: 2
单例对象:third: 3
普通对象:1
普通对象:1
普通对象:1
- 单例对象:不管调用多少次都是上一个对象,所以上面代码会一直累加
- 普通对象:每次创建新的对象,所以每次都为 1
2.2 伴生对象
若想同时用到包含实例方法和静态方法的类,可以用伴生对象实现,其特点如下:
- 单例对象与某个类具有相同名称时,它就被称为这个类的伴生对象
- 类与它的伴生对象必须存在同一文件中,且可以相互访问私有成员(字段和方法)
// 它是 TestObjectAndSample 类的伴生对象
object TestObjectAndSample {
private var lastId = 0
// 实现了静态方法类似的功能,类名.方法
private def newId() = {
lastId += 1
lastId
}
def main(args: Array[String]): Unit = {
val t1 = new TestObjectAndSample("rose")
val t2 = new TestObjectAndSample("lila")
t1.info()
t2.info()
}
}
class TestObjectAndSample {
private val id = TestObjectAndSample.newId() // 调用伴生对象的私有方法
private var name = ""
def this(name: String) {
this()
this.name = name
}
def info() = {
printf("the id is %d, the name is %s\n", id, name)
}
}
the id is 1, the name is rose
the id is 2, the name is lila
2.3 应用程序对象
每个 Scala
应用程序都必须从一个对象的main
方法开始,要运行应用程序有两种方式:
- 直接用
scala xxx.scala
运行:适用于只有一个单例对象的程序,没有定义其他类 - 先编译再运行
// HelloWorld.scala
// 只有一个单例对象,没有其他类
object HelloWorld {
def main(args: Array[String]): Unit = {
println("Hello World!")
}
}
// 方法一
F:\JavaStudy\ScalaStudy\src>scala HelloWorld.scala
Hello World!
// 方法二:先编译再运行,编译成功生成两个文件:HelloWorld$.class、HelloWorld.class
F:\JavaStudy\ScalaStudy\src>scalac HelloWorld.scala
F:\JavaStudy\ScalaStudy\src>scala HelloWorld
Hello World!
2.4 apply 方法和update 方法
符合以下约定时,将会隐式地调用 apply()、update()
方法:
-
用括号传递给变量(对象)一个或多个参数时,
Scala
会将其转换成对apply
方法的调用 -
当对带有括号并包括一到若干参数的对象进行赋值时,编译器将调用对象的
update
方法,调用时,是把括号中的参数和等号右边的对象一起作为update
方法的输入参数来执行调用
2.4.1 apply 方法
object ApplyAndUpdate {
def main(args: Array[String]): Unit = {
val tac = new TestApplyClass
// 给对象传递了一个参数,内部直接调用了类的 apply 方法
println(tac("rose"))
}
}
class TestApplyClass {
def apply(name: String): String = {
println("TestApplyClass 调用了 apply 方法!!!")
"Hello " + name
}
}
输出结果:
TestApplyClass 调用了 apply 方法!!!
Hello rose
单例对象 apply()
方法
object ApplyAndUpdate {
def main(args: Array[String]): Unit = {
// val tac = new TestApplyClass
// println(tac("rose"))
// 单例对象,将返回值赋值给 tsac
val tsac = TestSingleApplyClass("lila")
println(tsac)
}
}
class TestApplyClass {
def apply(name: String): String = {
println("TestApplyClass 调用了 apply 方法!!!")
"Hello " + name
}
}
object TestSingleApplyClass {
def apply(name: String): String = {
println("TestSingleApplyClass 调用了 apply 方法!!!")
"Hello " + name
}
}
伴生对象
object TestAndClassObject {
def main(args: Array[String]): Unit = {
val a = ApplyTest() // 调用伴生对象 apply 方法
a.otherMethod // 返回类 ApplyTest 的实例对象,所有可以直接调用 otherMethod() 方法
a() // 调用伴生类中的 apply 方法
}
}
class TestAndClassObject {
}
class ApplyTest {
def apply() = {
println("class ApplyTest apply is called!!!")
}
def otherMethod: Unit = {
println("class ApplyTest otherMethod is called!!!")
}
}
object ApplyTest {
def apply() = {
println("object ApplyTest apply is called!!!")
new ApplyTest() // 返回类 ApplyTest 的实例对象
}
}
输出结果:
object ApplyTest apply is called!!!
class ApplyTest otherMethod is called!!!
class ApplyTest apply is called!!!
2.4.2 update 方法
除了自定义的 apply
方法,还有一些内置的类在使用时,也会隐式地调用 apply/update
方法,如:
- 数组对象
Array
在初始化时不需要new
关键字、不需要构造器,直接传入参数就可以初始化,其内部调用的是apply
方法:val aArr = Array("rose", "lila", "john")
- 数组对象
Array
添加值,内部调用的update
方法
scala> val bArr = new Array[String](3)
bArr: Array[String] = Array(null, null, null)
scala> bArr(0) = "rose"
scala> bArr
res1: Array[String] = Array(rose, null, null)
scala> bArr(1) = "lila"
scala> bArr(2) = "john"
scala> bArr
res4: Array[String] = Array(rose, lila, john)
当对带有括号并包括一到若干参数的对象进行赋值时,编译器将调用对象的 update
方法,调用时,是把括号中的参数和等号右边的对象一起作为 update
方法的输入参数来执行调用
3. 继承
3.1 Scala 与 Java 继承的区别
区别:
- 重写一个非抽象方法必须使用
override
修饰符 - 只有主构造器可以调用超类的主构造器
- 在子类中重写超类的抽象方法时,不需要使用
override
关键字。 - 可以重写超类中的字段。
相同:
- 都不允许类从多个超类继承
3.2 抽象类
抽象类特点:
-
抽象类不能直接实例化
-
定义抽象类需要使用
abstract
关键字 -
抽象方法定义不需要
abstract
,只需把方法体空着就行 -
抽象字段定义:只要不初始化值就是一个抽象字段,但是必须定义字段类型,否则编译报错
abstract class Car {
val carBand: String // 字段没有初始化,就是抽象字段
def info() // 抽象方法
def abc(): Unit = {
println("Welcome to my Car!!!")
}
}
3.3 拓展类
继承(拓展)使用 extends
关键字:
- 重写超类抽象字段,要加
override
- 重写超类抽象方法,不用加
override
- 重写超类普通方法,要加
override
object AbstractClass {
def main(args: Array[String]): Unit = {
val bmw = new BMWCar
bmw.info()
bmw.abc()
}
}
// 抽象类不能直接实例化
abstract class Car {
val carBand: String // 字段没有初始化,就是抽象字段
def info() // 抽象方法
def abc(): Unit = {
println("Welcome to my Car!!!")
}
}
// 拓展类
class BMWCar extends Car {
// 重写超类抽象字段,要加 override
override val carBand = "BMW"
// 重写超类抽象方法,不用加 override
def info(): Unit = {
println("This is my BMW Car!!!")
}
// 重写超类普通方法,要加 override
override def abc(): Unit = {
println("Welcome to my BMW Car!!!")
}
}
运行结果:
This is my BMW Car!!!
Welcome to my BMW Car!!!
4. 特质
Scala
的特质类似于 Java
的接口,是代码重用的基本单元,可以同时拥有抽象方法和具体方法。另外通过特质可实现 多重继承:
- 定义特质:
trait
关键字 - 抽象方法不需要
abstract
关键字 - 没有方法体的的方法即为抽象方法
trait ClassName {
var id: Int // 抽象字段
def funName(): Int // 抽象方法
}
4.1 特质混入类中
将特质混入类中,实现类继承的功能(若特质只实现抽象字段、抽象方法就类似于 Java
接口):
object TraitClass {
def main(args: Array[String]): Unit = {
val bmw = new BMWCarId
println(bmw.info())
val byd = new BYDCarId
println(byd.info())
}
}
// 特质
trait CarId {
var id: Int // 抽象字段
def info(): Int // 抽象方法
}
class BMWCarId extends CarId {
override var id = 10001
def info(): Int = {
id + 1
}
}
class BYDCarId extends CarId {
override var id = 10002
override def info(): Int = {
id + 1
}
}
运行结果:
10002
10003
4.2 特质中实现具体方法
除了可以实现抽象字段和抽象方法外,特质中还可以实现具体的的方法:
object TraitClass {
def main(args: Array[String]): Unit = {
val bmw = new BMWCarId
println(bmw.info())
val byd = new BYDCarId
println(byd.info())
bmw.carName("BMW")
}
}
// 特质
trait CarId {
var id: Int
def info(): Int
def carName(name: String): Unit = {
println("Car name: " + name)
}
}
4.3 多个特质实现多重继承
一个类可以混入多个特质,使其实现多重继承的功能,使用方法:使用extends关键字混入第1个特质,后面可以反复使用with关键字混入更多特质
示例:
object TestTrait {
def main(args: Array[String]): Unit = {
val bmw = new BMW
bmw.info()
bmw.greeting("Welcome my second car.")
bmw.color("My Car color is black.")
}
}
trait CarIdTest {
var id: Int
def info(): Int
}
trait CarGreeting {
def greeting(name: String): Unit = {
println(name)
}
}
trait CarColor {
def color(name: String): Unit = {
println(name)
}
}
class BMW extends CarIdTest with CarGreeting with CarColor {
override var id: Int = 10001
def info(): Int = {
id + 1
}
// 重写普通方法
override def greeting(name: String): Unit = super.greeting(name)
override def color(name: String): Unit = super.color(name)
}
运行结果:
Welcome my second car.
My Car color is black.
5. 模式匹配
模式匹配类似于 Java
的 switch-case
语句,但是功能更为强大,可应用到 switch
语句、类型检查、结构等:
5.1 简单匹配
以下为一个简单的匹配整型的实例:
object TestMatch {
def main(args: Array[String]): Unit = {
val tm = new TestMatch
// 整型匹配
tm.intMatch()
}
}
class TestMatch {
def intMatch(): Unit = {
val score = 90
val info = score match {
case 70 => "D"
case 80 => "C"
case 90 => "B"
case 100 => "A"
case _ => score + "是不及格" // 没匹配返回的信息,这里还可以直接使用当前匹配的变量
}
println(info)
}
}
5.2 类型匹配
object TestMatch {
def main(args: Array[String]): Unit = {
val tm = new TestMatch
// 表达式匹配
tm.elemMatch()
}
}
class TestMatch {
def elemMatch(): Unit = {
for (elem <- List(1, 2.2, "Scala", "Python", "Java")) {
val str = elem match {
case i: Int => i + " is an Int Value"
case d: Double => d + " is an Double Value"
case "Scala" => "Scala is found"
case s: String => s + " is a String Value"
case _ => "This is an unexpected Value"
}
println(str)
}
}
}
输出结果:
1 is an Int Value
2.2 is an Double Value
Scala is found
Python is a String Value
Java is a String Value
5.3 guard 守卫语句
相比于 Java
,Scala
的 if
增加了两项强大的功能,一是可以直接作为赋值语句,另一种功能是作为守卫语句
if 作为赋值语句
作为赋值语句,替代了三元运算符,且支持逻辑语句块:
object IfTest {
def main(args: Array[String]): Unit = {
val (a, b) = (10, 22)
val value = if (a > b) 1 else if (a == b) {
val c = a - 1
println(c)
} else 0
println(value)
}
}
if作为for守卫语句
scala> val items = for (i <- -1 to 26 if i % 2 == 0 if i > 5) yield i
items: scala.collection.immutable.IndexedSeq[Int] = Vector(6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26)
scala> items
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26)
scala> val items = for (i <- -1 to 26 if i % 2 == 0 if i > 5) yield i *3
items: scala.collection.immutable.IndexedSeq[Int] = Vector(18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78)
if
作为for
守卫语句具有以下特点:
- 一个
for
循环可以支持多个if
语句,以空格、回车字符分割 - 多个
if
语句之间的关系是逻辑与&&
if
守卫语句并不支持其他循环语句,如:while
object TestMatch {
def main(args: Array[String]): Unit = {
val tm = new TestMatch
// 守卫 guard 语句
tm.guardMatch()
}
}
class TestMatch {
// 判断奇偶
def guardMatch(): Unit = {
for (elem <- List(1, 2, 3, 4, 5, 6, 7, 8)) {
elem match {
case _ if (elem % 2 == 0) => println(elem + " is even.")
case _ => println(elem + " is odd.")
}
}
}
}
if 作为 case 守卫语句
object IfTest {
def main(args: Array[String]): Unit = {
val items = for (i <- -1 to 26) yield i
val xs = items match {
case x if x.size < 1 => null
case x if x.contains(8) => 8
}
println(xs)
}
}
对于case
语句,虽然没有显式的 break
语句,但是依旧是“满足即阻断”,只会执行最先满足的 case
5.4 其他模式匹配
其他模式匹配还包括:
for
表达式中的模式case
类的匹配option
类型
5.4.1 for 表达式
scala> val info = Map("name" -> "rose", "age" -> 18)
info: scala.collection.immutable.Map[String,Any] = Map(name -> rose, age -> 18)
scala> for ((k, v) <- info) println(k, v)
(name,rose)
(age,18)
scala> for ((kv) <- info) println(kv._1, kv._2)
(name,rose)
(age,18)
5.4.2 case 类
case
类是一种特殊的类,它们经过优化以被用于模式匹配:
object IfTest {
def main(args: Array[String]): Unit = {
case class Animal(color: String, age: Int)
val fish = new Animal("white", 1)
val duck = new Animal("black", 2)
val dog = new Animal("yellow", 3)
for (item <- List(fish, duck, dog)) {
item match {
case Animal("white", 1) => println("This is a fish!")
case Animal("black", 2) => println("This is a duck!")
case Animal("yellow", 3) => println("This is a dog!")
case _ => println("no animal")
}
}
}
}
5.4.3 option 类
Option
类型用case
类来表示那种可能存在、也可能不存在的值,Option
有个 some
子类,当被引用的对象不存在时,可以使用 some
子类返回默认值,而不是无意义的 None
或 Null
Option[T]
是一个类型为 T 的可选值的容器: 如果值存在, Option[T]
就是一个 Some[T]
,如果不存在, Option[T]
就是对象 None
object OptionTest {
def main(args: Array[String]): Unit = {
val info = Map("name" -> "rose", "age" -> 18)
println("show name: " + show(info.get("name")))
println("show gender: " + show(info.get("gender")))
}
def show(x: Option[Any]) = x match {
case Some(s) => s
case None => "no such field"
}
}
运行结果:
show name: rose
show gender: no such field
getOrElse() 方法
scala> val info = Map("name" -> "rose", "age" -> 18)
info: scala.collection.immutable.Map[String,Any] = Map(name -> rose, age -> 18)
// 不存在的 键返回 None 对象,若取到了则放在 some 中返回
scala> val gender = info.get("gender")
gender: Option[Any] = None
// 指定默认值,使在获取不存在 key 时不返回 None 对象
scala> gender.getOrElse("info no gender")
res6: Any = info no gender
scala> println(gender.getOrElse("info no gender"))
info no gender
Option
实际上是一个容器,可以看做为一个集合,只不过是这个集合值包含一个元素(被包装在 Some
中返回),因为集合的一些常用也可以使用,如:map、foreach、filter
等:
scala> info.get("gender").foreach(println)
isEmpty 方法
isEmpty()
方法来检测元组中的元素是否为 None
:
object OptionTest {
def main(args: Array[String]): Unit = {
val a: Option[Int] = Some(22)
val b: Option[Int] = None
println(a) // Some(22)
println(b) // None
println(a.getOrElse(0)) //22
println(b.getOrElse(26)) //26
println(a.isEmpty) // false
println(b.isEmpty) // true
}
}