标签(空格分隔): Scala
By Vinfly
·函数调用方式:在scala中,函数调用也很简单。
·例如,import scala.math._,sqrt(2),pow(2, 4),min(3, Pi)。
·不同的一点是,如果调用函数时,不需要传递参数,则scala允许调用函数时省略括号的,例如,”Hello World”.distinct
·apply函数
·Scala中的apply函数是非常特殊的一种函数,在Scala的object中,可以声明apply函数。而使用“类名()”的形式,其实就是“类名.apply()”的一种缩写。通常使用这种方式来构造类的对象,而不是使用“new 类名()”的方式。
·例如,”Hello World”(6),因为在StringOps类中有def apply(n: Int):Char的函数定义,所以”Hello World”(6),实际上是”Hello World”.apply(6)的缩写。
·例如,Array(1, 2, 3, 4),实际上是用Array object的apply()函数来创建Array类的实例,也就是一个数组。
if(a < 10) {
b = b + 1
c = c + 1
}
·块表达式:块表达式,指的就是{}中的值,其中可以包含多条语句,最后一个语句的值就是块表达式的返回值。
·例如,var d = if(a < 10) { b = b + 1; c + 1 }
- 输入和输出
·print和println:print打印时不会加换行符,而println打印时会加一个换行符。
·例如,print(“Hello World”); println(“Hello World”)
·printf:printf可以用于进行格式化
·例如,printf(“Hi, my name is %s, I’m %d years old.\n”, “Leo”, 30)
readLine: readLine允许我们从控制台读取用户输入的数据,类似于java中的System.in和Scanner的作用
·综合案例:游戏厅门禁
val name = readLine("Welcome to Game House. Please tell me your name: ")
print("Thanks. Then please tell me your age: ")
val age = readInt()
if(age > 18) {
printf("Hi, %s, you are %d years old, so you are legel to come here!", name, age)
} else {
printf("Sorry, boy, %s, you are only %d years old. you are illegal to come here!", name, age)
}
var n = 10
while(n > 0) {
println(n)
n -= 1
}
·Scala没有for循环,只能使用while替代for循环,或者使用简易版的for语句
·简易版for语句:var n = 10; for(i <- 1 to n) println(i)
·或者使用until,表式不达到上限:for(i <- 1 until n) println(i)
·也可以对字符串进行遍历,类似于java的增强for循环,for(c <- “Hello World”) print(c)
·跳出循环语句
·scala没有提供类似于java的break语句。
·但是可以使用boolean类型变量、return或者Breaks的break函数来替代使用。
import scala.util.control.Breaks._
breakable {
var n = 10
for(c <- "Hello World") {
if(n == 5) break;
print(c)
n -= 1
}
}
for(i <- 1 to 9; j <- 1 to 9) {
if(j == 9) {
println(i * j)
} else {
print(i * j + " ")
}
}
·if守卫:取偶数
for(i <- 1 to 100 if i % 2 == 0) println(i)
·for推导式:构造集合
for(i <- 1 to 10) yield i
def sayHello(name: String, age: Int) = {
if (age > 18) { printf("hi %s, you are a big boy\n", name); age }
else { printf("hi %s, you are a little boy\n", name); age
}
调用:sayHello(“leo”, 30)
Scala要求必须给出所有参数的类型,但是不一定给出函数返回值的类型,只要右侧的函数体中不包含递归的语句,Scala就可以自己根据右侧的表达式推断出返回类型。
如果函数体中有多行代码,则可以使用代码块的方式包裹多行代码,代码块中最后一行的返回值就是整个函数的返回值。与Java中不同,不是使用return返回值的。
比如如下的函数,实现累加的功能:
def sum(n: Int) = {
var sum = 0;
for(i <- 1 to n) sum += i
sum
}
例如,实现经典的斐波那契数列:
9 + 8; 8 + 7 + 7 + 6; 7 + 6 + 6 + 5 + 6 + 5 + 5 + 4; ….
def fab(n: Int): Int = {
if(n <= 1) 1
else fab(n - 1) + fab(n - 2)
}
def sayHello(firstName: String, middleName: String = "William", lastName: String = "Croft") = firstName + " " + middleName + " " + lastName
如果给出的参数不够,则会从作往右依次应用参数。
带名参数:
在调用函数时,也可以不按照函数定义的参数顺序来传递参数,而是使用带名参数的方式来传递。
sayHello(firstName = "Mick", lastName = "Nina", middleName = "Jack")
还可以混合使用未命名参数和带名参数,但是未命名参数必须排在带名参数前面。
sayHello("Mick", lastName = "Nina", middleName = "Jack")
def sum(nums: Int*) = {
var res = 0
for (num <- nums) res += num
res
}
在如果想要将一个已有的序列直接调用变长参数函数,是不对的。
比如val s = sum(1 to 5)
此时需要使用Scala特殊的语法将参数定义为序列,让Scala解释器能够识别。这种语法非常有用!
val s = sum(1 to 5: _*)
案例:使用递归函数实现累加
def sum2(nums: Int*): Int = {
if (nums.length == 0) 0
else nums.head + sum2(nums.tail: _*)
}
def sayHello(name: String) { print("Hello, " + name); "Hello, " + name }
def sayHello1(name: String): Unit = "Hello, " + name
import scala.io.Source._
lazy val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString
// 数组初始化后,长度就固定下来了,而且元素全部根据其类型初始化
// 可以直接使用Array()创建数组,元素类型自动推断
在Scala中,如果需要类似于Java中的ArrayList这种长度可变的集合类,则可以使用ArrayBuffer。
// 如果不想每次都使用全限定名,则可以预先导入ArrayBuffer类
import scala.collection.mutable.ArrayBuffer
// 使用ArrayBuffer()的方式可以创建一个空的ArrayBuffer
val b = ArrayBuffer[Int]()
// 使用+=操作符,可以添加一个元素,或者多个元素
b += 1
b += (2, 3, 4, 5)
// 使用++=操作符,可以添加其他集合中的所有元素
b ++= Array(6, 7, 8, 9, 10)
// 使用trimEnd()函数,可以从尾部截断指定个数的元素
b.trimEnd(5)
// 使用insert()函数可以在指定位置插入元素
// 但是这种操作效率很低,因为需要移动指定位置后的所有元素
b.insert(5, 6)
b.insert(6, 7, 8, 9, 10)
// 使用remove()函数可以移除指定位置的元素
b.remove(1)
b.remove(1, 3)
// Array与ArrayBuffer可以互相进行转换
b.toArray
a.toBuffer
for (i <- 0 until b.length)
println(b(i))
for(i <- 0 until (b.length, 2))
println(b(i))
for(i <- (0 until b.length).reverse)
println(b(i))
val a = Array(1, 2, 3, 4, 5)
val a2 = for (ele <- a) yield ele * ele
val b = ArrayBuffer[Int]()
b += (1, 2, 3, 4, 5)
val b2 = for (ele <- b) yield ele * ele
val a3 = for (ele <-a if ele % 2 == 0) yield ele * ele
a.filter(_ % 2 == 0).map(2 * _)
a.filter { _ % 2 == 0 } map { 2 * _ }
// 构建数组
val a = ArrayBuffer[Int]()
a += (1, 2, 3, 4, 5, -1, -3, -5, -9)
// 每发现一个第一个负数之后的负数,就进行移除,性能较差,多次移动数组
var foundFirstNegative = false
var arrayLength = a.length
var index = 0
while (index < arrayLength) {
if (a(index) >= 0) {
index += 1
} else {
if (!foundFirstNegative) { foundFirstNegative = true; index += 1 }
else { a.remove(index); arrayLength -= 1 }
}
}
改良版
// 每记录所有不需要移除的元素的索引,稍后一次性移除所有需要移除的元素
// 性能较高,数组内的元素迁移只要执行一次即可
var foundFirstNegative = false
val keepIndexes = for (i <- 0 until a.length if !foundFirstNegative || a(i) >= 0) yield {
if (a(i) < 0) foundFirstNegative = true
i
}
//结果1
for (i <- 0 until keepIndexes.length) { a(i) = a(keepIndexes(i)) }
a.trimEnd(a.length - keepIndexes.length)
//结果2
创建Map
// 创建一个不可变的Map
val ages = Map("Leo" -> 30, "Jen" -> 25, "Jack" -> 23)
ages("Leo") = 31
// 创建一个可变的Map
val ages = scala.collection.mutable.Map("Leo" -> 30, "Jen" -> 25, "Jack" -> 23)
ages("Leo") = 31
// 使用另外一种方式定义Map元素
val ages = Map(("Leo", 30), ("Jen", 25), ("Jack", 23))
// 创建一个空的HashMap
val ages = new scala.collection.mutable.HashMap[String, Int]
访问Map
// 获取指定key对应的value,如果key不存在,会报错
val leoAge = ages("Leo")
val leoAge = ages("leo")
// 使用contains函数检查key是否存在
val leoAge = if (ages.contains("leo")) ages("leo") else 0
// getOrElse函数
val leoAge = ages.getOrElse("leo", 0)
//该函数优雅简洁
修改Map
// 更新Map的元素(可变)
ages("Leo") = 31
// 增加多个元素
ages += ("Mike" -> 35, "Tom" -> 40)
// 移除元素
ages -= "Mike"
// 更新不可变的map
val ages2 = ages + ("Mike" -> 36, "Tom" -> 40)
// 移除不可变map的元素
val ages3 = ages - "Tom"
遍历Map
// 遍历map的entrySet
for ((key, value) <- ages) println(key + " " + value)
// 遍历map的key
for (key <- ages.keySet) println(key)
// 遍历map的value
for (value <- ages.values) println(value)
// 生成新map,反转key和value
for ((key, value) <- ages) yield (value, key)
特殊Map
// SortedMap可以自动对Map的key的排序
val ages = scala.collection.immutable.SortedMap("leo" -> 30, "alice" -> 15, "jen" -> 25)
// LinkedHashMap可以记住插入entry的顺序
val ages = new scala.collection.mutable.LinkedHashMap[String, Int]
Tuple
// Tuple是一个元组,但不一定是两个,可以是多个
val t = (“leo”, 30)
// 访问Tuple
t._1
// zip操作
val names = Array(“leo”, “jack”, “mike”)
val ages = Array(30, 24, 26)
val nameAges = names.zip(ages)
for ((name, age) <- nameAges) println(name + “: ” + age)
1.定义一个简单的类
// 定义类,包含field以及方法
class HelloWorld {
private var name = "leo"
def sayHello() { print("Hello, " + name) }
def getName = name
}
// 创建类的对象,并调用其方法
val helloWorld = new HelloWorld
//也可以不加括号,如果定义方法时不带括号,则调用方法时也不能带括号
helloWorld.sayHello()
print(helloWorld.getName)
2.getter&setter
// 定义不带private的var field,此时scala生成的面向JVM的类时,会定义为private的name字段,并提供public的getter和setter方法
// 而如果使用private修饰field,则生成的getter和setter也是private的
// 如果定义val field,则只会生成getter方法
// 如果不希望生成setter和getter方法,则将field声明为private[this]
class Student {
var name = "leo"
}
// 调用getter和setter方法,分别叫做name和name_ =
val leo = new Student
print(leo.name)
leo.name = "leo1"
// 如果只是希望拥有简单的getter和setter方法,那么就按照scala提供的语法规则,根据需求为field选择合适的修饰符就好:var、val、private、private[this]
// 但是如果希望能够自己对getter与setter进行控制,则可以自定义getter与setter方法
// 自定义setter方法的时候一定要注意scala的语法限制,签名、=、参数间不能有空格
class Student {
private var myName = "leo"
def name = "your name is " + myName
def name_=(newValue: String) {
print("you cannot edit your name!!!")
}
}
val leo = new Student
print(leo.name)
leo.name = "leo1"
// 如果你不希望field有setter方法,则可以定义为val,但是此时就再也不能更改field的值了
// 但是如果希望能够仅仅暴露出一个getter方法,并且还能通过某些方法更改field的值,那么需要综合使用private以及自定义getter方法
// 此时,由于field是private的,所以setter和getter都是private,对外界没有暴露;自己可以实现修改field值的方法;自己可以覆盖getter方法
class Student {
private var myName = "leo"
def updateName(newName: String) {
if(newName == "leo1") myName = newName
else print("not accept this new name!!!")
}
def name = "your name is " + myName
}
// 如果将field使用private来修饰,那么代表这个field是类私有的,在类的方法中,可以直接访问类的其他对象的private field
// 这种情况下,如果不希望field被其他对象访问到,那么可以使用private[this],意味着对象私有的field,只有本对象内可以访问到
class Student {
private var myAge = 0
def age_=(newValue: Int) {
if (newValue > 0) myAge = newValue
else print("illegal age!")
}
def age = myAge
def older(s: Student) = {
myAge > s.myAge
}
}
3.辅助constructor
// Scala中,可以给类定义多个辅助constructor,类似于java中的构造函数重载
// 辅助constructor之间可以互相调用,而且必须第一行调用主constructor
class Student {
private var name = ""
private var age = 0
def this(name: String) {
this()
this.name = name
}
def this(name: String, age: Int) {
this(name)
this.age = age
}
}
4.主constructor
// Scala中,主constructor是与类名放在一起的,与java不同
// 而且类中,没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码,这点感觉没有java那么清晰
class Student(val name: String, val age: Int) {
println("your name is " + name + ", your age is " + age)
}
// 主constructor中还可以通过使用默认参数,来给参数默认的值
class Student(val name: String = "leo", val age: Int = 30) {
println("your name is " + name + ", your age is " + age)
}
// 如果主constrcutor传入的参数什么修饰都没有,比如name: String,那么如果类内部的方法使用到了,则会声明为private[this] name;否则没有该field,就只能被constructor代码使用而已
5.内部类
// Scala中,同样可以在类中定义内部类;但是与java不同的是,每个外部类的对象的内部类,都是不同的类
import scala.collection.mutable.ArrayBuffer
class Class {
class Student(val name: String) {}
val students = new ArrayBuffer[Student]
def getStudent(name: String) = {
new Student(name)
}
}
val c1 = new Class
val s1 = c1.getStudent("leo")
c1.students += s1
val c2 = new Class
val s2 = c2.getStudent("leo")
c1.students += s2
object Person {
private var eyeNum = 2
println("this Person object!")
def getEyeNum = eyeNum
}
object Person {
private val eyeNum = 2
def getEyeNum = eyeNum
}
class Person(val name: String, val age: Int) {
def sayHello = println("Hi, " + name + ", I guess you are " + age + " years old!" + ", and usually you must have " + Person.eyeNum + " eyes.")
}
abstract class Hello(var message: String) {
def sayHello(name: String): Unit
}
object HelloImpl extends Hello("hello") {
override def sayHello(name: String) = {
println(message + ", " + name)
}
}
// 比如,Array类的伴生对象的apply方法就实现了接收可变数量的参数,并创建一个Array对象的功能
val a = Array(1, 2, 3, 4, 5)
// 比如,定义自己的伴生类和伴生对象
class Person(val name: String)
object Person {
def apply(name: String) = new Person(name)
}
object HelloWorld {
def main(args: Array[String]) {
println("Hello World!!!")
}
}
*// 除了自己实现main方法之外,还可以继承App Trait,然后将需要在main方法中运行的代码,直接作为object的constructor代码;而且用args可以接受传入的参数
object HelloWorld extends App {
if (args.length > 0) println(“hello, ” + args(0))
else println(“Hello World!!!”)
}
// 如果要运行上述代码,需要将其放入.scala文件,然后先使用scalac编译,再用scala执行
scalac HelloWorld.scala
scala -Dscala.time HelloWorld
// App Trait的工作原理为:App Trait继承自DelayedInit Trait,scalac命令进行编译时,会把继承App Trait的object的constructor代码都放到DelayedInit Trait的delayedInit方法中执行*
object Season extends Enumeration {
val SPRING, SUMMER, AUTUMN, WINTER = Value
}
object Season extends Enumeration {
val SPRING = Value(0, "spring")
val SUMMER = Value(1, "summer")
val AUTUMN = Value(2, "autumn")
val WINTER = Value(3, "winter")
}
Season(0)
Season.withName("spring")
for (ele <- Season.values) println(ele)
class Person {
private var name = "leo"
def getName = name
}
class Student extends Person {
private var score = "A"
def getScore = score
}
override & super
// Scala中,如果子类要覆盖一个父类中的非抽象方法,则必须使用override关键字
// override关键字可以帮助我们尽早地发现代码里的错误,比如:override修饰的父类方法的方法名我们拼写错了;比如要覆盖的父类方法的参数我们写错了;等等
// 此外,在子类覆盖父类方法之后,如果我们在子类中就是要调用父类的被覆盖的方法呢?那就可以使用super关键字,显式地指定要调用父类的方法
class Person {
private var name = "leo"
def getName = name
}
class Student extends Person {
private var score = "A"
def getScore = score
override def getName = "Hi, I'm " + super.getName
}
// Scala中,子类可以覆盖父类的val field,而且子类的val field还可以覆盖父类的val field的getter方法;只要在子类中使用override关键字即可
class Person {
val name: String = "Person"
def age: Int = 0
}
class Student extends Person {
override val name: String = "leo"
override val age: Int = 30
}
class Person
class Student extends Person
val p: Person = new Student
var s: Student = null
if (p.isInstanceOf[Student]) s = p.asInstanceOf[Student]
class Person
class Student extends Person
val p: Person = new Student
p.isInstanceOf[Person]
p.getClass == classOf[Person]
p.getClass == classOf[Student]
使用模式匹配进行类型判断
// 但是在实际开发中,比如spark的源码中,大量的地方都是使用了模式匹配的方式来进行类型的判断,这种方式更加地简洁明了,而且代码得可维护性和可扩展性也非常的高
// 使用模式匹配,功能性上来说,与isInstanceOf一样,也是判断主要是该类以及该类的子类的对象即可,不是精准判断的
class Person
class Student extends Person
val p: Person = new Student
p match {
case per: Person => println("it's Person's object")
case _ => println("unknown type")
}
protected
// 跟java一样,scala中同样可以使用protected关键字来修饰field和method,这样在子类中就不需要super关键字,直接就可以访问field和method
// 还可以使用protected[this],则只能在当前子类对象中访问父类的field和method,无法通过其他子类对象访问父类的field和method
class Person {
protected var name: String = "leo"
protected[this] var hobby: String = "game"
}
class Student extends Person {
def sayHello = println("Hello, " + name)
def makeFriends(s: Student) {
println("my hobby is " + hobby + ", your hobby is " + s.hobby)
}
}
class Person(val name: String, val age: Int)
class Student(name: String, age: Int, var score: Double) extends Person(name, age) {
def this(name: String) {
this(name, 0, 0)
}
def this(age: Int) {
this("leo", age, 0)
}
}
匿名内部类
// 在Scala中,匿名子类是非常常见,而且非常强大的。Spark的源码中也大量使用了这种匿名子类。
// 匿名子类,也就是说,可以定义一个类的没有名称的子类,并直接创建其对象,然后将对象的引用赋予一个变量。之后甚至可以将该匿名子类的对象传递给其他函数。
class Person(protected val name: String) {
def sayHello = "Hello, I'm " + name
}
val p = new Person("leo") {
override def sayHello = "Hi, I'm " + name
}
def greeting(p: Person { def sayHello: String }) {
println(p.sayHello)
}
抽象类
// 如果在父类中,有某些方法无法立即实现,而需要依赖不同的子来来覆盖,重写实现自己不同的方法实现。此时可以将父类中的这些方法不给出具体的实现,只有方法签名,这种方法就是抽象方法。
// 而一个类中如果有一个抽象方法,那么类就必须用abstract来声明为抽象类,此时抽象类是不可以实例化的
// 在子类中覆盖抽象类的抽象方法时,不需要使用override关键字
abstract class Person(val name: String) {
def sayHello: Unit
}
class Student(name: String) extends Person(name) {
def sayHello: Unit = println("Hello, " + name)
}
抽象field
// 如果在父类中,定义了field,但是没有给出初始值,则此field为抽象field
// 抽象field意味着,scala会根据自己的规则,为var或val类型的field生成对应的getter和setter方法,但是父类中是没有该field的
// 子类必须覆盖field,以定义自己的具体field,并且覆盖抽象field,不需要使用override关键字
abstract class Person {
val name: String
}
class Student extends Person {
val name: String = "leo"
}
trait 作为接口
// Scala中的Triat是一种特殊的概念
// 首先我们可以将Trait作为接口来使用,此时的Triat就与Java中的接口非常类似
// 在triat中可以定义抽象方法,就与抽象类中的抽象方法一样,只要不给出方法的具体实现即可
// 类可以使用extends关键字继承trait,注意,这里不是implement,而是extends,在scala中没有implement的概念,无论继承类还是trait,统一都是extends
// 类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字
// scala不支持对类进行多继承,但是支持多重继承trait,使用with关键字即可
trait HelloTrait {
def sayHello(name: String)
}
trait MakeFriendsTrait {
def makeFriends(p: Person)
}
class Person(val name: String) extends HelloTrait with MakeFriendsTrait with Cloneable with Serializable {
def sayHello(name: String) = println("Hello, " + name)
def makeFriends(p: Person) = println("Hello, my name is " + name + ", your name is " + p.name)
}
在Trait中定义具体方法
// Scala中的Triat可以不是只定义抽象方法,还可以定义具体方法,此时trait更像是包含了通用工具方法的东西
// 有一个专有的名词来形容这种情况,就是说trait的功能混入了类
// 举例来说,trait中可以包含一些很多类都通用的功能方法,比如打印日志等等,spark中就使用了trait来定义了通用的日志打印方法
trait Logger {
def log(message: String) = println(message)
}
class Person(val name: String) extends Logger {
def makeFriends(p: Person) {
println("Hi, I'm " + name + ", I'm glad to make friends with you, " + p.name)
log("makeFriends methdo is invoked with parameter Person[name=" + p.name + "]")
}
}
在Trait中定义具体字段
// Scala中的Triat可以定义具体field,此时继承trait的类就自动获得了trait中定义的field
// 但是这种获取field的方式与继承class是不同的:如果是继承class获取的field,实际是定义在父类中的;而继承trait获取的field,就直接被添加到了类中
trait Person {
val eyeNum: Int = 2
}
class Student(val name: String) extends Person {
def sayHello = println("Hi, I'm " + name + ", I have " + eyeNum + " eyes.")
}
4.在Trait中定义抽象字段
// Scala中的Triat可以定义抽象field,而trait中的具体方法则可以基于抽象field来编写
// 但是继承trait的类,则必须覆盖抽象field,提供具体的值
trait SayHello {
val msg: String
def sayHello(name: String) = println(msg + ", " + name)
}
class Person(val name: String) extends SayHello {
val msg: String = "hello"
def makeFriends(p: Person) {
sayHello(p.name)
println("I'm " + name + ", I want to make friends with you!")
}
}
5.为实例混入Trait
// 有时我们可以在创建类的对象时,指定该对象混入某个trait,这样,就只有这个对象混入该trait的方法,而类的其他对象则没有
trait Logged {
def log(msg: String) {}
}
trait MyLogger extends Logged {
override def log(msg: String) { println("log: " + msg) }
}
class Person(val name: String) extends Logged {
def sayHello { println("Hi, I'm " + name); log("sayHello is invoked!") }
}
val p1 = new Person("leo")
p1.sayHello
val p2 = new Person("jack") with MyLogger
p2.sayHello
7.trait调用链
// Scala中支持让类继承多个trait后,依次调用多个trait中的同一个方法,只要让多个trait的同一个方法中,在最后都执行super.方法即可
// 类中调用多个trait中都有的这个方法时,首先会从最右边的trait的方法开始执行,然后依次往左执行,形成一个调用链条
// 这种特性非常强大,其实就相当于设计模式中的责任链模式的一种具体实现依赖
trait Handler {
def handle(data: String) {}
}
trait DataValidHandler extends Handler {
override def handle(data: String) {
println("check data: " + data)
super.handle(data)
}
}
trait SignatureValidHandler extends Handler {
override def handle(data: String) {
println("check signature: " + data)
super.handle(data)
}
}
class Person(val name: String) extends SignatureValidHandler with DataValidHandler {
def sayHello = { println("Hello, " + name); handle(name) }
}
8.在trait中覆盖抽象方法
// 在trait中,是可以覆盖父trait的抽象方法的
// 但是覆盖时,如果使用了super.方法的代码,则无法通过编译。因为super.方法就会去掉用父trait的抽象方法,此时子trait的该方法还是会被认为是抽象的
// 此时如果要通过编译,就得给子trait的方法加上abstract override修饰
trait Logger {
def log(msg: String)
}
trait MyLogger extends Logger {
abstract override def log(msg: String) { super.log(msg) }
}
9.混合使用trait的具体方法和抽象方法
// 在trait中,可以混合使用具体方法和抽象方法
// 可以让具体方法依赖于抽象方法,而抽象方法则放到继承trait的类中去实现
// 这种trait其实就是设计模式中的模板设计模式的体现
trait Valid {
def getName: String
def valid: Boolean = {
getName == "leo"
}
}
class Person(val name: String) extends Valid {
println(valid)
def getName = name
}
trait 的构造机制
// 在Scala中,trait也是有构造代码的,也就是trait中的,不包含在任何方法中的代码
// 而继承了trait的类的构造机制如下:1、父类的构造函数执行;2、trait的构造代码执行,多个trait从左到右依次执行;3、构造trait时会先构造父trait,如果多个trait继承同一个父trait,则父trait只会构造一次;4、所有trait构造完毕之后,子类的构造函数执行
class Person { println("Person's constructor!") }
trait Logger { println("Logger's constructor!") }
trait MyLogger extends Logger { println("MyLogger's constructor!") }
trait TimeLogger extends Logger { println("TimeLogger's constructor!") }
class Student extends Person with MyLogger with TimeLogger {
println("Student's constructor!")
}
11.trait field 的初始化
// 在Scala中,trait是没有接收参数的构造函数的,这是trait与class的唯一区别,但是如果需求就是要trait能够对field进行初始化,该怎么办呢?只能使用Scala中非常特殊的一种高级特性——提前定义
trait SayHello {
val msg: String
println(msg.toString)
}
class Person
val p = new {
val msg: String = "init"
} with Person with SayHello
//输出1
class Person extends {
val msg: String = "init"
} with SayHello {}
//输出2
// 另外一种方式就是使用lazy value
trait SayHello {
lazy val msg: String = null
println(msg.toString)
}
class Person extends SayHello {
override lazy val msg: String = "init"
}
//输出3
11.trait继承class
// 在Scala中,trait也可以继承自class,此时这个class就会成为所有继承该trait的类的父类
class MyUtil {
def printMessage(msg: String) = println(msg)
}
trait Logger extends MyUtil {
def log(msg: String) = printMessage("log: " + msg)
}
class Person(val name: String) extends Logger {
def sayHello {
log("Hi, I'm " + name)
printMessage("Hi, I'm " + name)
}
}
Scala中的函数是Java中完全没有的概念。因为Java是完全面向对象的编程语言,没有任何面向过程编程语言的特性,因此Java中的一等公民是类和对象,而且只有方法的概念,即寄存和依赖于类和对象中的方法。Java中的方法是绝对不可能脱离类和对象独立存在的。
而Scala是一门既面向对象,又面向过程的语言。因此在Scala中有非常好的面向对象的特性,可以使用Scala来基于面向对象的思想开发大型复杂的系统和工程;而且Scala也面向过程,因此Scala中有函数的概念。在Scala中,函数与类、对象等一样,都是一等公民。Scala中的函数可以独立存在,不需要依赖任何类和对象。
Scala的函数式编程,就是Scala面向过程的最好的佐证。也正是因为函数式编程,才让Scala具备了Java所不具备的更强大的功能和特性。
而之所以Scala一直没有替代Java,是因为Scala之前一直没有开发过太多知名的应用;而Java则不一样,Java诞生的非常早,上个世界90年代就诞生了,基于Java开发了大量知名的工程。而且最重要的一点在于,Java现在不只是一门编程语言,还是一个庞大的,涵盖了软件开发,甚至大数据、云计算的技术生态,Java生态中的重要框架和系统就太多了:Spring、Lucene、Activiti、Hadoop等等
1. 将函数赋值给变量
// Scala中的函数是一等公民,可以独立定义,独立存在,而且可以直接将函数作为值赋值给变量
// Scala的语法规定,将函数赋值给变量时,必须在函数后面加上空格和下划线
def sayHello(name: String) { println("Hello, " + name) }
val sayHelloFunc = sayHello _
sayHelloFunc("leo")
2.匿名函数
// Scala中,函数也可以不需要命名,此时函数被称为匿名函数。// 可以直接定义函数之后,将函数赋值给某个变量;也可以将直接定义的匿名函数传入其他函数之中
// Scala定义匿名函数的语法规则就是,(参数名: 参数类型) => 函数体
// 这种匿名函数的语法必须深刻理解和掌握,在spark的中有大量这样的语法,如果没有掌握,是看不懂spark源码的
val sayHelloFunc = (name: String) => println(“Hello, ” + name)
3.高阶函数
// Scala中,由于函数是一等公民,因此可以直接将某个函数传入其他函数,作为参数。这个功能是极其强大的,也是Java这种面向对象的编程语言所不具备的。
// 接收其他函数作为参数的函数,也被称作高阶函数(higher-order function)
val sayHelloFunc = (name: String) => println("Hello, " + name)
def greeting(func: (String) => Unit, name: String) { func(name) }
greeting(sayHelloFunc, "leo")
Array(1, 2, 3, 4, 5).map((num: Int) => num * num)
def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name)
val greetingFunc = getGreetingFunc("hello")
greetingFunc("leo")
4.高阶函数的类型判断
// 高阶函数可以自动推断出参数类型,而不需要写明类型;而且对于只有一个参数的函数,还可以省去其小括号;如果仅有的一个参数在右侧的函数体内只使用一次,则还可以将接收参数省略,并且将参数用_来替代
// 诸如3 * _的这种语法,必须掌握!!spark源码中大量使用了这种语法!
def greeting(func: (String) => Unit, name: String) { func(name) }
greeting((name: String) => println("Hello, " + name), "leo")
greeting((name) => println("Hello, " + name), "leo")
greeting(name => println("Hello, " + name), "leo")
def triple(func: (Int) => Int) = { func(3) }
triple(3 * _)
5. Scala的常用高阶函数
// map: 对传入的每个元素都进行映射,返回一个处理后的元素
Array(1, 2, 3, 4, 5).map(2 * _)
// foreach: 对传入的每个元素都进行处理,但是没有返回值
(1 to 9).map("*" * _).foreach(println _)
// filter: 对传入的每个元素都进行条件判断,如果对元素返回true,则保留该元素,否则过滤掉该元素
(1 to 20).filter(_ % 2 == 0)
// reduceLeft: 从左侧元素开始,进行reduce操作,即先对元素1和元素2进行处理,然后将结果与元素3处理,再将结果与元素4处理,依次类推,即为reduce;reduce操作必须掌握!spark编程的重点!!!
// 下面这个操作就相当于1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9
(1 to 9).reduceLeft( _ * _)
// sortWith: 对元素进行两两相比,进行排序
Array(3, 2, 5, 4, 10, 1).sortWith(_ < _)
6.闭包
// 闭包最简洁的解释:函数在变量不处于其有效作用域时,还能够对变量进行访问,即为闭包
def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name)
val greetingFuncHello = getGreetingFunc("hello")
val greetingFuncHi = getGreetingFunc("hi")
// 两次调用getGreetingFunc函数,传入不同的msg,并创建不同的函数返回
// 然而,msg只是一个局部变量,却在getGreetingFunc执行完之后,还可以继续存在创建的函数之中;greetingFuncHello(“leo”),调用时,值为”hello”的msg被保留在了函数体内部,可以反复的使用
// 这种变量超出了其作用域,还可以使用的情况,即为闭包
// Scala通过为每个函数创建对象来实现闭包,实际上对于getGreetingFunc函数创建的函数,msg是作为函数对象的变量存在的,因此每个函数才可以拥有不同的msg
// Scala编译器会确保上述闭包机制
7.Currying函数
// Curring函数,指的是,将原来接收两个参数的一个函数,转换为两个函数,第一个函数接收原先的第一个参数,然后返回接收原先第二个参数的第二个函数。
// 在函数调用的过程中,就变为了两个函数连续调用的形式
// 在Spark的源码中,也有体现,所以对()()这种形式的Curring函数,必须掌握!
def sum(a: Int, b: Int) = a + b
sum(1, 1)
def sum2(a: Int) = (b: Int) => a + b
sum2(1)(1)
def sum3(a: Int)(b: Int) = a + b
8. return
// Scala中,不需要使用return来返回函数的值,函数最后一行语句的值,就是函数的返回值。在Scala中,return用于在匿名函数中返回值给包含匿名函数的带名函数,并作为带名函数的返回值。
// 使用return的匿名函数,是必须给出返回类型的,否则无法通过编译
def greeting(name: String) = {
def sayHello(name: String):String = {
return "Hello, " + name
}
sayHello(name)
}
// Scala中的集合是分成可变和不可变两类集合的,其中可变集合就是说,集合的元素可以动态修改,而不可变集合的元素在初始化之后,就无法修改了。分别对应scala.collection.mutable和scala.collection.immutable两个包。
// Seq下包含了Range、ArrayBuffer、List等子trait。其中Range就代表了一个序列,通常可以使用“1 to 10”这种语法来产生一个Range。 ArrayBuffer就类似于Java中的ArrayList。
// 案例:用递归函数来给List中每个元素都加上指定前缀,并打印加上前缀的元素
def decorator(l: List[Int], prefix: String) {
if (l != Nil) {
println(prefix + l.head)
decorator(l.tail, prefix)
}
}
LinkedList
// LinkedList代表一个可变的列表,使用elem可以引用其头部,使用next可以引用其尾部
// val l = scala.collection.mutable.LinkedList(1, 2, 3, 4, 5); l.elem; l.next
// 案例:使用while循环将LinkedList中的每个元素都乘以2
val list = scala.collection.mutable.LinkedList(1, 2, 3, 4, 5)
var currentList = list
while (currentList != Nil) {
currentList.elem = currentList.elem * 2
currentList = currentList.next
}
// 案例:使用while循环将LinkedList中,从第一个元素开始,每隔一个元素,乘以2
val list = scala.collection.mutable.LinkedList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
var currentList = list
var first = true
while (currentList != Nil && currentList.next != Nil) {
if (first) { currentList.elem = currentList.elem * 2; first = false }
currentList = currentList.next.next
if (currentList != Nil) currentList.elem = currentList.elem * 2
}
Set
// Set代表一个没有重复元素的集合
// 将重复元素加入Set是没有用的,比如val s = Set(1, 2, 3); s + 1; s + 4
// 而且Set是不保证插入顺序的,也就是说,Set中的元素是乱序的,val s = new scala.collection.mutable.HashSet[Int](); s += 1; s += 2; s += 5
// LinkedHashSet会用一个链表维护插入顺序,val s = new scala.collection.mutable.LinkedHashSet[Int](); s += 1; s += 2; s += 5
// SrotedSet会自动根据key来进行排序,val s = scala.collection.mutable.SortedSet("orange", "apple", "banana")
集合的函数式编程
// 必须完全掌握和理解Scala的高阶函数是什么意思,Scala的集合类的map、flatMap、reduce、reduceLeft、foreach等这些函数,就是高阶函数,因为可以接收其他函数作为参数
// 高阶函数的使用,也是Scala与Java最大的一点不同!!!因为Java里面是没有函数式编程的,也肯定没有高阶函数,也肯定无法直接将函数传入一个方法,或者让一个方法返回一个函数
// 对Scala高阶函数的理解、掌握和使用,可以大大增强你的技术,而且也是Scala最有诱惑力、最有优势的一个功能!!!
// map案例实战:为List中每个元素都添加一个前缀
List(“Leo”, “Jen”, “Peter”, “Jack”).map(“name is ” + _)
// faltMap案例实战:将List中的多行句子拆分成单词
List(“Hello World”, “You Me”).flatMap(_.split(” “))
// foreach案例实战:打印List中的每个单词
List(“I”, “have”, “a”, “beautiful”, “house”).foreach(println(_))
// zip案例实战:对学生姓名和学生成绩进行关联
List(“Leo”, “Jen”, “Peter”, “Jack”).zip(List(100, 90, 75, 83))
// 案例:成绩评价
def judgeGrade(grade: String) {
grade match {
case "A" => println("Excellent")
case "B" => println("Good")
case "C" => println("Just so so")
case _ => println("you need work harder")
}
}
在模式匹配中使用if守卫
// Scala的模式匹配语法,有一个特点在于,可以在case后的条件判断中,不仅仅只是提供一个值,而是可以在值后面再加一个if守卫,进行双重过滤
// 案例:成绩评价(升级版)
def judgeGrade(name: String, grade: String) {
grade match {
case "A" => println(name + ", you are excellent")
case "B" => println(name + ", you are good")
case "C" => println(name + ", you are just so so")
case _ if name == "leo" => println(name + ", you are a good boy, come on")
case _ => println("you need to work harder")
}
}
// Scala的模式匹配语法,有一个特点在于,可以将模式匹配的默认情况,下划线,替换为一个变量名,此时模式匹配语法就会将要匹配的值赋值给这个变量,从而可以在后面的处理语句中使用要匹配的值
// 为什么有这种语法??思考一下。因为只要使用用case匹配到的值,是不是我们就知道这个只啦!!在这个case的处理语句中,是不是就直接可以使用写程序时就已知的值!
// 但是对于下划线这种情况,所有不满足前面的case的值,都会进入这种默认情况进行处理,此时如果我们在处理语句中需要拿到具体的值进行处理呢?那就需要使用这种在模式匹配中进行变量赋值的语法!!
// 案例:成绩评价(升级版)
def judgeGrade(name: String, grade: String) {
grade match {
case "A" => println(name + ", you are excellent")
case "B" => println(name + ", you are good")
case "C" => println(name + ", you are just so so")
case _grade if name == "leo" => println(name + ", you are a good boy, come on, your grade is " + _grade)
case _grade => println("you need to work harder, your grade is " + _grade)
}
}
- 对类型进行模式匹配
// Scala的模式匹配一个强大之处就在于,可以直接匹配类型,而不是值!!!这点是java的switch case绝对做不到的。
// 理论知识:对类型如何进行匹配?其他语法与匹配值其实是一样的,但是匹配类型的话,就是要用“case 变量: 类型 => 代码”这种语法,而不是匹配值的“case 值 => 代码”这种语法。
// 案例:异常处理
import java.io._
def processException(e: Exception) {
e match {
case e1: IllegalArgumentException => println("you have illegal arguments! exception is: " + e1)
case e2: FileNotFoundException => println("cannot find the file you need read or write!, exception is: " + e2)
case e3: IOException => println("you got an error while you were doing IO operation! exception is: " + e3)
case _: Exception => println("cannot know which exception you have!" )
}
}
// 案例:对朋友打招呼
def greeting(arr: Array[String]) {
arr match {
case Array("Leo") => println("Hi, Leo!")
case Array(girl1, girl2, girl3) => println("Hi, girls, nice to meet you. " + girl1 + " and " + girl2 + " and " + girl3)
case Array("Leo", _*) => println("Hi, Leo, please introduce your friends to me.")
case _ => println("hey, who are you?")
}
}
def greeting(list: List[String]) {
list match {
case "Leo" :: Nil => println("Hi, Leo!")
case girl1 :: girl2 :: girl3 :: Nil => println("Hi, girls, nice to meet you. " + girl1 + " and " + girl2 + " and " + girl3)
case "Leo" :: tail => println("Hi, Leo, please introduce your friends to me.")
case _ => println("hey, who are you?")
}
}
// 案例:学校门禁
class Person
case class Teacher(name: String, subject: String) extends Person
case class Student(name: String, classroom: String) extends Person
def judgeIdentify(p: Person) {
p match {
case Teacher(name, subject) => println("Teacher, name is " + name + ", subject is " + subject)
case Student(name, classroom) => println("Student, name is " + name + ", classroom is " + classroom)
case _ => println("Illegal access, please go out of the school!")
}
}
Option与模式匹配
// Scala有一种特殊的类型,叫做Option。Option有两种值,一种是Some,表示有值,一种是None,表示没有值。
// Option通常会用于模式匹配中,用于判断某个变量是有值还是没有值,这比null来的更加简洁明了
// Option的用法必须掌握,因为Spark源码中大量地使用了Option,比如Some(a)、None这种语法,因此必须看得懂Option模式匹配,才能够读懂spark源码。
// 案例:成绩查询
val grades = Map("Leo" -> "A", "Jack" -> "B", "Jen" -> "C")
def getGrade(name: String) {
val grade = grades.get(name)
grade match {
case Some(grade) => println("your grade is " + grade)
case None => println("Sorry, your grade information is not in the system")
}
}
案例:新生报到,每个学生来自不同的地方,id可能是Int,可能是String
class Student[T](val localId: T) {
def getSchoolId(hukouId: T) = "S-" + hukouId + "-" + localId
}
val leo = new Student[Int](111)
案例:卡片售卖机,可以指定卡片的内容,内容可以是String类型或Int类型
def getCard[T](content: T) = {
if(content.isInstanceOf[Int]) "card: 001, " + content
else if(content.isInstanceOf[String]) "card: this is your card, " + content
else "card: " + content
}
getCard[String]("hello world")
// 案例:特殊售票窗口(只接受特殊人群,比如学生、老人等)
class SpecialPerson(val name: String)
class Student(val name: String)
class Older(val name: String)
implicit def object2SpecialPerson (obj: Object): SpecialPerson = {
if (obj.getClass == classOf[Student]) { val stu = obj.asInstanceOf[Student]; new SpecialPerson(stu.name) }
else if (obj.getClass == classOf[Older]) { val older = obj.asInstanceOf[Older]; new SpecialPerson(older.name) }
else Nil
}
var ticketNumber = 0
def buySpecialTicket(p: SpecialPerson) = {
ticketNumber += 1
"T-" + ticketNumber
}
// 案例:超人变身
class Man(val name: String)
class Superman(val name: String) {
def emitLaser = println("emit a laster!")
}
implicit def man2superman(man: Man): Superman = new Superman(man.name)
val leo = new Man("leo")
leo.emitLaser
// 1、调用某个函数,但是给函数传入的参数的类型,与函数定义的接收参数类型不匹配(案例:特殊售票窗口)
// 2、使用某个类型的对象,调用某个方法,而这个方法并不存在于该类型时(案例:超人变身)
// 3、使用某个类型的对象,调用某个方法,虽然该类型有这个方法,但是给方法传入的参数类型,与方法定义的接收参数的类型不匹配(案例:特殊售票窗口加强版)
// 案例:特殊售票窗口加强版
class TicketHouse {
var ticketNumber = 0
def buySpecialTicket(p: SpecialPerson) = {
ticketNumber += 1
"T-" + ticketNumber
}
}
// 案例:考试签到
class SignPen {
def write(content: String) = println(content)
}
implicit val signPen = new SignPen
def signForExam(name: String) (implicit signPen: SignPen) {
signPen.write(name + " come to exam in time.")
}
Scala的Actor类似于Java中的多线程编程。但是不同的是,Scala的Actor提供的模型与多线程有所不同。Scala的Actor尽可能地避免锁和共享状态,从而避免多线程并发时出现资源争用的情况,进而提升多线程编程的性能。此外,Scala Actor的这种模型还可以避免死锁等一系列传统多线程编程的问题。
Spark中使用的分布式多线程框架,是Akka。Akka也实现了类似Scala Actor的模型,其核心概念同样也是Actor。
1、Actor的创建、启动和消息收发(案例:Actor Hello World)
2、收发case class类型的消息(案例:用户注册登录后台接口)
3、Actor之间互相收发消息(案例:打电话)
4、同步消息和Future
// Scala提供了Actor trait来让我们更方便地进行actor多线程编程,就Actor trait就类似于Java中的Thread和Runnable一样,是基础的多线程基类和接口。我们只要重写Actor trait的act方法,即可实现自己的线程执行体,与Java中重写run方法类似。
// 此外,使用start()方法启动actor;使用!符号向actor发送消息;actor内部使用receive和模式匹配接收消息
// 案例:Actor Hello World
import scala.actors.Actor
class HelloActor extends Actor {
def act() {
while (true) {
receive {
case name: String => println("Hello, " + name)
}
}
}
}
val helloActor = new HelloActor
helloActor.start()
helloActor ! "leo"
// Scala的Actor模型与Java的多线程模型之间,很大的一个区别就是,Scala Actor天然支持线程之间的精准通信;即一个actor可以给其他actor直接发送消息。这个功能是非常强大和方便的。
// 要给一个actor发送消息,需要使用“actor ! 消息”的语法。在scala中,通常建议使用样例类,即case class来作为消息进行发送。然后在actor接收消息之后,可以使用scala强大的模式匹配功能来进行不同消息的处理。
// 案例:用户注册登录后台接口
case class Login(username: String, password: String)
case class Register(username: String, password: String)
class UserManageActor extends Actor {
def act() {
while (true) {
receive {
case Login(username, password) => println("login, username is " + username + ", password is " + password)
case Register(username, password) => println("register, username is " + username + ", password is " + password)
}
}
}
}
val userManageActor = new UserManageActor
userManageActor.start()
userManageActor ! Register("leo", "1234"); userManageActor ! Login("leo", "1234")
// 如果两个Actor之间要互相收发消息,那么scala的建议是,一个actor向另外一个actor发送消息时,同时带上自己的引用;其他actor收到自己的消息时,直接通过发送消息的actor的引用,即可以给它回复消息。
// 案例:打电话
case class Message(content: String, sender: Actor)
class LeoTelephoneActor extends Actor {
def act() {
while (true) {
receive {
case Message(content, sender) => { println("leo telephone: " + content); sender ! "I'm leo, please call me after 10 minutes." }
}
}
}
}
class JackTelephoneActor(val leoTelephoneActor: Actor) extends Actor {
def act() {
leoTelephoneActor ! Message("Hello, Leo, I'm Jack.", this)
receive {
case response: String => println("jack telephone: " + response)
}
}
}
默认情况下,消息都是异步的;但是如果希望发送的消息是同步的,即对方接受后,一定要给自己返回结果,那么可以使用!?的方式发送消息。即val reply = actor !? message。
如果要异步发送一个消息,但是在后续要获得消息的返回值,那么可以使用Future。即!!语法。val future = actor !! message。val reply = future()。
1、基于boolean类型的控制变量
2、使用嵌套函数以及return
3、使用Breaks类的break方法
//while循环:
var flag = true
var res = 0
var n = 0
while(flag) {
res += n
n += 1
if (n == 5) {
flag = false
}
}
//for循环:(高级for循环,加上了if守卫)
var flag = true
var res = 0
for (i <- 0 until 10 if flag) {
res += i
if (i == 4) flag = false
}
def add_outer() = {
var res = 0
def add_inner() {
for (i <- 0 until 10) {
if (i == 5) {
return
}
res += i
}
}
add_inner()
res
}
//跟java里面的break比较类似,相对来说,比较灵活好用;与breakable代码块配合使用
import scala.util.control.Breaks._
var res = 0
breakable {
for (i <- 0 until 10) {
if (i == 5) {
break;
}
res += i
}
}
什么是多维数组?:数组的元素,还是数组,数组套数组,就是多维数组
构造指定行与列的二维数组:Array.ofDim方法
val multiDimArr1 = Array.ofDim[Double](3, 4)
multiDimArr1(0)(0) = 1.0
构造不规则多维数组:
val multiDimArr2 = new Array[Array[Int]](3)
multiDimArr2(0) = new Array[Int] (1)
multiDimArr2(1) = new Array[Int] (2)
multiDimArr2(2) = new Array[Int] (3)
multiDimArr2(1)(1) = 1
//Scala代码中,直接调用JDK(Java)的API,比如调用一个Java类的方法,势必可能会传入Java类型的list;Scala中构造出来的list,其实是ArrayBuffer;你直接把Scala的ArrayBuffer传入Java接收ArrayList的方法,肯定不行。
import scala.collection.JavaConversions.bufferAsJavaList
import scala.collection.mutable.ArrayBuffer
val command = ArrayBuffer("javac", "C:\\Users\\Administrator\\Desktop\\HelloWorld.java")
val processBuilder = new ProcessBuilder(command)
val process = processBuilder.start()
val res = process.waitFor()
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable.Buffer
val cmd: Buffer[String] = processBuilder.command()
//Tuple拉链操作指的就是zip操作
//zip操作,是Array类的方法,用于将两个Array,合并为一个Array
//比如Array(v1)和Array(v2),使用zip操作合并后的格式为Array((v1,v2))
//合并后的Array的元素类型为Tuple
val students = Array("Leo", "Jack", "Jen")
val scores = Array(80, 100, 90)
val studentScores = students.zip(scores)
for ((student, score) <- studentScores)
println(student + " " + score)
//如果Array的元素类型是个Tuple,调用Array的toMap方法,可以将Array转换为Map
studentScores.toMap
import scala.collection.JavaConversions.mapAsScalaMap
val javaScores = new java.util.HashMap[String, Int]()
javaScores.put("Alice", 10)
javaScores.put("Bob", 3)
javaScores.put("Cindy", 8)
val scalaScores: scala.collection.mutable.Map[String, Int] = javaScores
import scala.collection.JavaConversions.mapAsJavaMap
import java.awt.font.TextAttribute._
val scalaAttrMap = Map(FAMILY -> "Serif", SIZE -> 12)
val font = new java.awt.Font(scalaAttrMap)
import scala.collection.mutable.ArrayBuffer
class Class {
class Student(val name: String)
val students = new ArrayBuffer[Student]
def register(name: String) = {
new Student(name)
}
}
val c1 = new Class
val leo = c1.register("leo")
c1.students += leo
val c2 = new Class
val jack = c2.register("jack")
c1.students += jack
object Class {
class Student(val name: String)
}
class Class {
val students = new ArrayBuffer[Class.Student]
def register(name: String) = {
new Class.Student(name)
}
}
val c1 = new Class
val leo = c1.register("leo")
c1.students += leo
val c2 = new Class
val jack = c2.register("jack")
c1.students += jack
class Class {
class Student(val name: String)
val students = new ArrayBuffer[Class#Student]
def register(name: String) = {
new Student(name)
}
}
val c1 = new Class
val leo = c1.register("leo")
c1.students += leo
val c2 = new Class
val jack = c2.register("jack")
c1.students += jack
class Class(val name: String) { outer =>
class Student(val name: String) {
def introduceMyself = "Hello, I'm " + name + ", I'm very happy to join class " + outer.name
}
def register(name: String) = {
new Student(name)
}
}
val c1 = new Class("c1")
val leo = c1.register("leo")
leo.introduceMyself
默认情况下,如果父类中的构造函数代码,用到了会被子类重写的field; 那么会出现令人意想不到的一幕:
1、子类的构造函数(无参)调用父类的构造函数(无参)
2、父类的构造函数初始化field(结果正确)
3、父类的构造函数使用field执行其他构造代码,但是此时其他构造代码如果使用了该field,而且field要被子类重写,那么它的getter方法被重写,返回0(比如Int)
4、子类的构造函数再执行,重写field(结果也正确)
5、但是此时子类从父类继承的其他构造代码,已经出现了错误了
class Student {
val classNumber: Int = 10
val classScores: Array[Int] = new Array[Int](classNumber)
}
class PEStudent extends Student{
override val classNumber: Int = 3
}
本来我们期望的是,PEStudent,可以从Student继承来一个长度为3的classScores数组
结果。。。PEStudent对象,只有一个长度为0的classScores数组
此时只能使用Scala对象继承的一个高级特性: 提前定义,在父类构造函数执行之前,先执行子类的构造函数中的某些代码
class PEStudent extends {
override val classNumber: Int = 3
} with Student
这里我们大概知道一下Scala的继承层级,我们写的所有的Scala trait和class,都是默认继承自一些Scala根类的,有一些基础的方法
Scala中,最顶端的两个trait是Nothing和Null,Null trait唯一的对象就是null
其次是继承了Nothing trait的Any类
接着Anyval trait和AnyRef类,都继承自Any类
Any类是个比较重要的类,其中定义了isInstanceOf和asInstanceOf等方法,以及equals、hashCode等对象的基本方法
Any类,有点像Java中的Object基类
AnyRef类,增加了一些多线程的方法,比如wait、notify/notifyAll、synchronized等,也是属于Java Object类的一部分
这里,我们要知道,在scala中,你如何判断两个引用变量,是否指向同一个对象实例
AnyRef的eq方法用于检查两个变量是否指向同一个对象实例
AnyRef的equals方法默认调用eq方法实现,也就是说,默认情况下,判断两个变量相等,要求必须指向同一个对象实例
通常情况下,自己可以重写equals方法,根据类的fields来判定是否相等
此外,定义equals方法时,也最好使用同样的fields,重写hashCode方法
如果只是想要简单地通过是否指向同一个对象实例,判定变量是否相当,那么直接使用==操作符即可,默认判断null,然后调用equals方法
class Product(val name: String, val price: Double) {
final override def equals(other: Any) = {
val that = other.asInstanceOf[Product]
if(that == null) false
else name == that.name && price == that.price
}
final override def hashCode = 13 * name.hashCode + 17 * price.hashCode
}
scala提供了io.Source类来操作文件,使用时需导入import scala.io.Source
案例:遍历文件的每一行
方法一:使用Source.getLines返回的迭代器
val source = Source.fromFile("C://Users//hp-pc//Desktop//test1.txt")
val lineIterator = source.getLines
for (line <- lineIterator) println(line)
方法二:将Source.getLines返回的迭代器,转换成数组
这里说明一点: 一个BufferedSource对象的getLines方法,只能调用一次,一次调用完之后,遍历了迭代器里所有的内容,就已经把文件里的内容读取完了
如果反复调用source.getLines,是获取不到内容的
此时,必须重新创建一个BufferedSource对象
val source = Source.fromFile("C://Users//hp-pc//Desktop//test1.txt")
val lines = source.getLines.toArray
for(line <- lines) println(line)
方法三:调用Source.mkString,返回文本中所有的内容
val source = Source.fromFile("C://Users//hp-pc//Desktop//test1.txt")
val lines = source.mkString
案例二:遍历文件中的每个字符
//BufferedSource,也实现了一个Iterator[Char]的这么一个trait
val source = Source.fromFile("C://Users//hp-pc//Desktop//test1.txt")
for(c <- source) print(c)
//使用完BufferedSource对象之后,调用BufferedSource.close方法,关闭IO流资源
案例三:从URL以及字符串中读取字符
val source = Source.fromURL("http://www.baidu.com","UTF-8")
val source = Source.fromString("Hello World")