這篇文章是我跟著視頻學,再加上看博客總結(jié)的Scala關鍵知識點,用來開發(fā)Spark完全夠用。
第一節(jié):基礎
- 變量聲明 var val
- 七種值類型(Byte,Char,Short,Int,Long,F(xiàn)loat,Double)
- 條件表達式
val y = if (x>1) 1 else "error"
Java中所有類的基類是Object,Scala中所有類的基類是Any
Unit相當于Java中的void
for循環(huán)
1 to 10 返回 1,2,3...10
1 until 10 返回1,2,3...9
for(i <- 1 to 10){
println(i)
}
for(i <- 1 until 10){
println(i)
}
//for循環(huán)遍歷集合
var a = 0;
val numList = List(1,2,3,4,5,6);
// for 循環(huán)
for( a <- numList ){
println( "Value of a: " + a );
}
//嵌套for循環(huán)的高級寫法&for循環(huán)過濾
for (i<- 1 to 3; j<- 1 to 3 if(i!=j)){
println(i*10+j)
}
- 方法和函數(shù)的聲明以及方法轉(zhuǎn)換成函數(shù)
Scala 有方法與函數(shù),二者在語義上的區(qū)別很小。Scala 方法是類的一部分,而函數(shù)是一個對象可以賦值給一個變量。換句話來說在類中定義的函數(shù)即是方法。
Scala 中使用 val 語句可以定義函數(shù),def 語句定義方法。
方法定義格式如下:
def functionName ([參數(shù)列表]) : [return type] = {
function body
return [expr]
}
//聲明求兩數(shù)之和的方法:
def addInt( a:Int, b:Int ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
函數(shù)定義格式如下:
val addInt = (x: Int,y: Int) => x + y
第二節(jié) 數(shù)組、映射、元組、集合
- 定長數(shù)組
val arr1 = new Array[Int](8)
var arr2 = Array("java", "scala")
//小括號下標
println(arr2(0))
- 變長數(shù)組
注意要引入import scala.collection.mutable.ArrayBuffer
然后使用ArrayBuffer 類
追加操作: += ; ++= ; insert方法
常用方法: reverse、delete、sum、max、min、sorted、yield關鍵字
- 映射
創(chuàng)建映射
//用靜態(tài)類創(chuàng)建
val map1 =Map("scala"-> 1, "java"->2, "python"->3)
//用元組創(chuàng)建
val map2 = Map(("scala",1),("java",2),("python",3))
獲取值
map1("scala")
可變映射:
//必須要引用這個
import scala.collection.mutable.Map
map1("scala")=6
常用函數(shù):getOrElse
- 元組
K-V對的集合
val t = ("scala", 100L, 3.14, ("spark",1))
//取元組的第一個位置的值,“scala”
t._1
note:元組的取值下標從1開始。
//這樣可以用a,b,c,d去取元組的值
val t,(a,b,c,d) = ("scala", 100L, 3.14, ("spark",1))
println(a)
兩個方法:
toMap用于把數(shù)組(元素是元組)轉(zhuǎn)換成不可變的Map
zip拉鏈操作。用于把多個Array的值對應起來生成一個新的Array,新的Array內(nèi)容為元組。
arr1 = Array(24,25,26)
arr2 = Array("a","b","c")
arr1.zip(arr2)
如果兩個數(shù)組的長度不等,那么就會把長的數(shù)組的后面截取掉。
- 集合
Seq(序列)
val list1 = List(1,2,3)
val list2 = 0:: list1 //在List頭部添加0
val list3 = 0 +:list1 //也是在List頭部添加0
val list6 = list1 :+ 4 //在List尾部部添加4
合并兩個List:使用++ ++: :::
可變List:使用ListBuffer類
Set(集合)
集合有去重功能
import scala.collection.immutable.HashSet
val set1 = new HashSet[Int ]()
不可變集合可以使用++合并兩個Set
可變集合使用add +=追加元素
可變集合使用 ++=合并兩個集合
可變集合使用 -= remove刪除元素
Map(映射)
val map1 = new HashMap[String,Int]()
map1("scala")=1
map1 += (("java",2))
map1. put("C++",5)
map1 - ="java"
map1 . remove("C++")
第三節(jié) 函數(shù)式編程
lazy關鍵字修飾的是惰性變量
-
map
-
filter
-
flatMap
-
reduce
-
fold
-
aggregate
交并差集
union、intersect、diff實現(xiàn)wordcount
object Test2{
def main(args: Array[String]): Unit = {
val list = List("hello java hello scala hello python","hello java hello scala hello python")
val words = list.flatMap(_.split(" "))
val tuples = words.map( x=>(x,1) )
val grouped = tuples.groupBy( (_._1) )
val mapvalues = grouped.mapValues(_.size)
println(grouped)
}
}
第四節(jié) 面向?qū)ο?/h2>
- 創(chuàng)建類、屬性
package day01
class Person{
val id = "100"
var name:String = _
//只有本類才能訪問,伴生對象也可以訪問
private var age = 120
//只有本類才能訪問,伴生對象訪問不到
private [this] val gender = "男"
}
//伴生對象
object Person{
def main(args: Array[String]): Unit = {
val p = new Person()
//可以修改var變量
p.age=200
println(p.age)
}
}
object Test1{
def main(args: Array[String]): Unit = {
val p = new Person
//p.age出錯,因為是private
//p.age
}
}
- 構造器、輔助構造器
//主構造器的參數(shù)列表要放到類名的后面,和類名放在一起
//此時的faceValue:Int只能在本類調(diào)用,伴生對象也無法調(diào)用,雖然沒有用val或var修飾,但默認是val
class StructDemo(val name:String, var age:Int, faceValue:Int=90) {
var gender:String = _
def getFaceValue():Int={
return faceValue
}
//輔助構造器
def this(name:String,age:Int,faceValue:Int,gender:String){
this(name,age,faceValue) //輔助構造器第一行必須先調(diào)用主構造器
this.gender = gender
}
}
object StructDemo{
def main(args: Array[String]): Unit = {
//val s = new StructDemo("ningning",26,98)
//println(s.faceValue) //訪問不到,因為沒用val或var修飾
val s = new StructDemo("ningning",26,98,"女")
println(s.name)
println(s.age)
val face = s.getFaceValue()
println(face)
println(s.gender)
}
}
- 單例對象
package day01
class Person{
val id = "100"
var name:String = _
//只有本類才能訪問,伴生對象也可以訪問
private var age = 120
//只有本類才能訪問,伴生對象訪問不到
private [this] val gender = "男"
}
//伴生對象
object Person{
def main(args: Array[String]): Unit = {
val p = new Person()
//可以修改var變量
p.age=200
println(p.age)
}
}
object Test1{
def main(args: Array[String]): Unit = {
val p = new Person
//p.age出錯,因為是private
//p.age
}
}
//主構造器的參數(shù)列表要放到類名的后面,和類名放在一起
//此時的faceValue:Int只能在本類調(diào)用,伴生對象也無法調(diào)用,雖然沒有用val或var修飾,但默認是val
class StructDemo(val name:String, var age:Int, faceValue:Int=90) {
var gender:String = _
def getFaceValue():Int={
return faceValue
}
//輔助構造器
def this(name:String,age:Int,faceValue:Int,gender:String){
this(name,age,faceValue) //輔助構造器第一行必須先調(diào)用主構造器
this.gender = gender
}
}
object StructDemo{
def main(args: Array[String]): Unit = {
//val s = new StructDemo("ningning",26,98)
//println(s.faceValue) //訪問不到,因為沒用val或var修飾
val s = new StructDemo("ningning",26,98,"女")
println(s.name)
println(s.age)
val face = s.getFaceValue()
println(face)
println(s.gender)
}
}
就是用object修飾的類
創(chuàng)建對象的時候可以不用new
直接 val test = Object1
調(diào)用里面的方法直接類名.方法名即可
- 伴生對象
與類名相同,并且用object修飾的對象叫伴生對象
類和伴生對象之間可以相互訪問私有的方法和屬性
- apply、unapply方法
apply方法經(jīng)常用在伴生對象中,用來構造對象而不用顯式地使用new。
unapply是當做是伴生對象的apply方法的反向操作。apply方法接受構造參數(shù),然后將他們變成對象。而unapply方法接受一個對象,然后從中提取值。unapply方法返回的是一個Option.
object ScalaRunner {
def main(args: Array[String]): Unit = {
testApply2()
testApplyUnApply()
testCaseClass()
testNumber()
testUnapplyCheck()
}
private def testUnapplyCheck(): Unit = {
"Hello World fdf" match {
case Name(first, last@IsCompound()) => println(s"first: ${first}, last: ${last}")
}
}
private def testNumber() = {
val Number(n) = "12345"
println(s"n: ${n}")
}
private def testCaseClass(): Unit = {
val p: Person = Person("Sky", 20)
p match {
case Person(name, 20) => println(s"name: ${name}")
}
}
private def testApplyUnApply() {
val nameObj = Name("hello", "world")
println(s"a: ${nameObj.a}, b: ${nameObj.b}")
val Name(arg11, arg12) = "Hello World"
println(s"arg11: ${arg11}, arg12: ${arg12}")
val Name(arg21, arg22) = Name("hello", "world").toString
println(s"arg21: ${arg21}, arg22: ${arg22}")
}
private def testApply(): Unit = {
Name()
(new Name) ()
(new Name()) ()
Name()()
}
private def testApply2(): Unit = {
Name(1)
(new Name()) (1)
(new Name) (1)
Name(1)(1)
}
}
case class Person(val name: String, val age: Int)
object Number {
def unapply(input: String): Option[Int] = {
try{
Some(Integer.parseInt(input.trim))
} catch {
case ex: NumberFormatException => None
}
}
}
class Name {
var a = ""
var b = ""
def this(a: String, b: String){
this
println("Call class construct.")
this.a = a
this.b = b
}
def apply() = {
println("Call class apply.")
}
def apply(i: Int) = {
println(s"Call class apply ${i}.")
}
override def toString: String = {
a + " " + b
}
}
object Name {
def apply(): Name = {
println("Call object apply.")
new Name()
}
def apply(i: Int): Name = {
println(s"Call object apply ${i}.")
new Name()
}
def apply(a: String, b: String): Name = {
println(s"Call object apply.")
new Name(a, b)
}
def unapply(input: String): Option[(String, String)] = {
println(s"Call object unapply.")
val pos = input.indexOf(" ")
if(pos == -1) None
else Some((input.substring(0, pos), input.substring(pos + 1)))
}
}
object IsCompound {
def unapply(input: String) = {
println(s"Call IsCompound unapply ${input}.")
input.contains(" ")
}
}
運行結(jié)果:

- private關鍵字
參考資料:Scala訪問權限修飾符:private和private[this]
作為參考和對比,首先從Java開始。
在Java中,方法可以訪問該類的所有對象的私有字段,例如:
public class Person {
private String name;
private int age; // 該字段無getter/setter方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private String getName() { // 該私有方法只是為了和Scala對比
return name;
}
private void setName(String name) { // 該私有方法只是為了和Scala對比
this.name = name;
}
public boolean equals(Person other) {
return this.name.equals(other.name) && this.age == other.age;
}
}
Person類的equals方法內(nèi),能夠直接訪問其他Person對象的私有字段(other.name/other.age)。
在Scala中,方法也可以訪問該類的所有對象的私有字段,稱為類私有字段;
但在Scala中,允許定義更加嚴格的字段訪問控制,通過private[this]來實現(xiàn),稱為對象私有字段,即只能在對象內(nèi)部訪問的字段,請看下圖:

在圖中,job屬于類私有字段,salary屬于對象私有字段;
方法內(nèi)不能訪問其他對象的對象私有字段(salary)。
Scala編譯器分別對private和private[this] 都做了什么?
首先對gao.Person.scala文件做一下修改,如下:
class Person(val name:String, var age:Int) {
private var job = "Programmer"
private[this] var salary = 3000F
}
然后編譯該文件,再使用javap查看編譯后的Person.class文件,可以看到:
$ javap -p target/scala-2.11/classes/gao/Person.class
Compiled from "Person.scala"
public class gao.Person {
private final java.lang.String name;
private int age;
private java.lang.String job; // private修飾,生成的private的getter/setter方法
private float salary; // private修飾,沒有生成getter/setter方法
public java.lang.String name();
public int age();
public void age_$eq(int);
private java.lang.String job(); // job字段的private的getter方法
private void job_$eq(java.lang.String); // job字段的private的setter方法
public gao.Person(java.lang.String, int);
}
- 特質(zhì)
scala中的特質(zhì)可以類比成Java中的接口:
1.特質(zhì)中定義的方法可以實現(xiàn),【有了大括號的就是已經(jīng)實現(xiàn)過的方法,例如下面Animal中的listen和run】;也可以不實現(xiàn)【例如Animal類中的speak方法】
package cookBook.chapter8
trait Animal{
//沒有實現(xiàn)
def speak
def listen: Unit ={
}
def run: Unit ={
println("I'm running")
}
}
class People extends Animal{
override def speak: Unit ={
println("I'm speaking English")
}
}
object People extends App{
var people = new People
people.speak
people.listen//don't have result
people.run
}
運行結(jié)果如下:
I'm speaking English
I'm running
同時,如果我們將上面類People中的speak給刪除,會顯示報錯。class People must either be declared abstract or implements abstract members...。這就是說:
2.如果一個類實現(xiàn)特質(zhì),那么必須實現(xiàn)特質(zhì)中未定義的方法。否則這個類應該為抽象類。scala中有抽象類,但是更加傾向于使用特質(zhì)。
3.scala中可以實現(xiàn)一個類同時繼承多個特質(zhì)
package cookBook.chapter8
//輪胎
trait tire{
def run: Unit ={
println("I can run fast")
}
}
//方向盤
trait SteeringWheel{
def control: Unit ={
println("I can control the cars'direction")
}
}
//同時繼承多個特質(zhì)
//使用with,后面可接多個with
class Roadster extends tire with SteeringWheel {
def display(): Unit ={
println("I'm a Roadster")
}
}
//敞篷跑車
object Roadster extends App{
var roadster = new Roadster
roadster.display()
roadster.run
roadster.control
}
運行結(jié)果如下:
I'm a Roadster
I can run fast
I can control the cars'direction
注:特質(zhì)構造順序
1、首先調(diào)用超類的構造器
2、然后調(diào)用特質(zhì)構造器,特質(zhì)構造器在超類構造器之后,類構造器之前
3、特質(zhì)由左到右被構造
4、在每個特質(zhì)當中,父特質(zhì)先被構造
5、如果多個特質(zhì)公用一個父特質(zhì),而那個父特質(zhì)已經(jīng)被構造過了,則不會再被構造
6、所有特質(zhì)構造完畢,子類被構造
介紹特質(zhì)比較詳細的一篇文章https://www.cnblogs.com/nowgood/p/scalatrait.html
- 抽象類
//抽象類
abstract class Animal1{
//抽象字段
var name:String
var size:Int
//抽象方法
def walk
}
//抽象類實現(xiàn)類
class Cat(var length:Int)extends Animal1{
override var name = "cat"
override var size = 100
override def walk{
println(this.name + ":" + this.size + ":" + this.length)
}
}
object AbstractClassTest {
def main(args: Array[String]): Unit = {
val cat = new Cat(200)
cat.walk
println("name:" + cat.name)
println("size:" + cat.size)
println("length:" + cat.length)
}
}
cat:100:200
name:cat
size:100
length:200
Scala抽象類不能被實例化,包含若干定義不完全的方法,具體的實現(xiàn)由子類去實現(xiàn)。
第五章 模式匹配
以下部分來自:菜鳥教程
Scala 提供了強大的模式匹配機制,應用也非常廣泛。
一個模式匹配包含了一系列備選項,每個都開始于關鍵字 case。每個備選項都包含了一個模式及一到多個表達式。箭頭符號 => 隔開了模式和表達式。以下是一個簡單的整型值模式匹配實例:
object Test {
def main(args: Array[String]) {
println(matchTest(3))
}
def matchTest(x: Int): String = x match {
case 1 => "one"
case 2 => "two"
case _ => "many"
}
}
執(zhí)行以上代碼,輸出結(jié)果為:
$ scalac Test.scala
$ scala Test
many
match 對應 Java 里的 switch,但是寫在選擇器表達式之后。即: 選擇器 match {備選項}。match 表達式通過以代碼編寫的先后次序嘗試每個模式來完成計算,只要發(fā)現(xiàn)有一個匹配的case,剩下的case不會繼續(xù)匹配。接下來我們來看一個不同數(shù)據(jù)類型的模式匹配:
object Test {
def main(args: Array[String]) {
println(matchTest("two"))
println(matchTest("test"))
println(matchTest(1))
println(matchTest(6))
}
def matchTest(x: Any): Any = x match {
case 1 => "one"
case "two" => 2
case y: Int => "scala.Int"
case _ => "many"
}
}
執(zhí)行以上代碼,輸出結(jié)果為:
$ scalac Test.scala
$ scala Test
2
many
one
scala.Int
實例中第一個 case 對應整型數(shù)值 1,第二個 case 對應字符串值 two,第三個 case 對應類型模式,用于判斷傳入的值是否為整型,相比使用isInstanceOf來判斷類型,使用模式匹配更好。第四個 case 表示默認的全匹配備選項,即沒有找到其他匹配時的匹配項,類似 switch 中的 default。
- 樣例類
使用了case關鍵字的類定義就是就是樣例類(case classes),樣例類是種特殊的類,經(jīng)過優(yōu)化以用于模式匹配。
以下是樣例類的簡單實例:
object Test {
def main(args: Array[String]) {
val alice = new Person("Alice", 25)
val bob = new Person("Bob", 32)
val charlie = new Person("Charlie", 32)
for (person <- List(alice, bob, charlie)) {
person match {
case Person("Alice", 25) => println("Hi Alice!")
case Person("Bob", 32) => println("Hi Bob!")
case Person(name, age) =>
println("Age: " + age + " year, name: " + name + "?")
}
}
}
// 樣例類
case class Person(name: String, age: Int)
}
執(zhí)行以上代碼,輸出結(jié)果為:
$ scalac Test.scala
$ scala Test
Hi Alice!
Hi Bob!
Age: 32 year, name: Charlie?
在聲明樣例類時,下面的過程自動發(fā)生了:
構造器的每個參數(shù)都成為val,除非顯式被聲明為var,但是并不推薦這么做;
在伴生對象中提供了apply方法,所以可以不使用new關鍵字就可構建對象;
提供unapply方法使模式匹配可以工作;
生成toString、equals、hashCode和copy方法,除非顯示給出這些方法的定義。
- 偏函數(shù)
第六節(jié) 柯里化、隱式轉(zhuǎn)換

有關逆變協(xié)變
https://blog.csdn.net/zero__007/article/details/52245475
這篇文章是講Java的。不過意思一樣。
文章中提到了PECS原則,就是什么時候使用逆變協(xié)變的。
隱式轉(zhuǎn)換
scala的隱式轉(zhuǎn)換學習總結(jié)(詳細)





