1. 程式人生 > >Spark基礎-scala學習(二)

Spark基礎-scala學習(二)

面向物件程式設計之類

//定義一個簡單的類
scala> :paste
// Entering paste mode (ctrl-D to finish)
//類預設public的
class HelloWorld{
  private var name = "leo"
  def sayHello(){print("Hello,"+name)}
  def getName = name
}

// Exiting paste mode, now interpreting.

defined class HelloWorld

scala> var helloWorld = new HelloWorld
helloWorld: HelloWorld = 
[email protected]
scala> helloWorld.sayHello() Hello,leo scala> print(helloWorld.getName) //也可以不加括號,如果定義方法時不帶括號,則用方法時也不能帶括號 leo
//getter與setter
//定義不帶private的var field,JVM會自動定義為private,並提供public的getter和setter方法
//如果定義private 修飾field,則生成getter和setter也是private的
//如果定義val filed,則只會生成getter方法
//如果不希望生成setter和getter方法,則將field宣告為private[this]

自定義getter與setter

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student{
 private var myName = "leo"
 def name = "your name is" + myName
 def name_=(newValue:String){
  print("you cannot edit your name!!")
}
}

// Exiting paste mode, now interpreting.

defined class Student

scala> val leo = new Student //()可有可無
leo: Student = 
[email protected]
scala> print(leo.name) your name isleo scala> leo.name = "leo1" //相當於setter方法 you cannot edit your name!!leo.name: String = your name isleo

僅暴露field的getter方法

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student{
 private var myName = "leo" //private修飾,不暴露getter和setter方法,因此可以自定義
 def updateName(newName:String){
  if(newName == "leo1") myName = newName
  else print("not accpet this new name!!")
}
def name = "your name is "+myName
}

// Exiting paste mode, now interpreting.

defined class Student

scala> val s = new Student()
s: Student = [email protected]

scala> s.name
res30: String = your name is leo

scala> s.updateName("tom")
not accpet this new name!!
scala> s.updateName("leo1")

scala> s.name
res33: String = your name is leo1

private[this]的使用

scala> :paste
// Entering paste mode (ctrl-D to finish)

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)={ //用private[this]修飾的話會報錯
  myAge > s.myAge
 }
}

// Exiting paste mode, now interpreting.

defined class Student

scala> val s1 = new Student
s1: Student = [email protected]

scala> s1.age = 20
s1.age: Int = 20

scala> val s2 = new Student
s2: Student = [email protected]

scala> s2.age = 25
s2.age: Int = 25

scala> s1.older(s2)
res34: Boolean = false

java風格的getter和setter

// 在Scala 2.10.0之後已被廢棄
// 使用scala.beans.BeanProperty代替
scala> import scala.beans.BeanProperty
import scala.beans.BeanProperty
scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student{
  @BeanProperty var name:String = _
}

// Exiting paste mode, now interpreting.

defined class Student

scala> val s = new Student
s: Student = [email protected]

scala> s.setName("leo")

scala> s.get
getClass   getName

scala> s.getName
res39: String = leo

輔助建構函式constructor

scala> :paste
// Entering paste mode (ctrl-D to finish)

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
  }
}

// Exiting paste mode, now interpreting.

defined class Student

scala> var s1 = new Student()
s1: Student = [email protected]

scala> val s2 = new Student("leo")
s2: Student = [email protected]

scala> val s3 = new Student("leo",30)
s3: Student = [email protected]

主建構函式constructor

//主constructor與類名放在一起,與java不同
//類中沒有定義在任何方法或者程式碼塊中的程式碼,就是主constructor的程式碼

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student(val name:String,val age:Int){
  println("your name is "+name +",your age is "+age)
}

// Exiting paste mode, now interpreting.

defined class Student

scala> val s = new Student
<console>:12: error: not enough arguments for constructor Student: (name: String, age: Int)Student.
Unspecified value parameters name, age.
       val s = new Student
               ^

scala> val s = new Student("jom",23)
your name is jom,your age is 23
s: Student = [email protected]

//給預設值
scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student(val name:String="leo",val age:Int=30){ // 如果主constructor傳入的引數什麼修飾都沒有,比如name:String ,那麼如果類內部方法使用到了,則會宣告為private[this] name;否則沒有該field,就只能被constructor程式碼使用而已
  println(name+" "+age)
}

// Exiting paste mode, now interpreting.

defined class Student

scala> val s1 = new Student
leo 30
s1: Student = [email protected]

內部類

scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Class{
  class Student(val name:String){}
  val students = new ArrayBuffer[Student]
  def getStudent(name:String)={
   new Student(name)
  }
}

// Exiting paste mode, now interpreting.

defined class Class

scala> val c1 = new Class
c1: Class = [email protected]

scala> val s1 = c1.getStudent("leo")
s1: c1.Student = [email protected]

scala> c1.students += s1
res0: c1.students.type = ArrayBuffer([email protected])

scala> val c2 = new Class
c2: Class = [email protected]

scala> val s2 = c2.getStudent("leo")
s2: c2.Student = [email protected]

scala> c1.students += s2
<console>:15: error: type mismatch;
 found   : c2.Student
 required: c1.Student
       c1.students += s2
                      ^

面向物件程式設計之物件

  1. object,相當於class的單個例項,通常在裡面放一些靜態的field或者method
  2. 第一次呼叫object的方法時,就會執行object的constructor,也就是object內部不在method中的程式碼;但是object不能定義接收引數的constructor
  3. 注意,object的constructor只會在第一次被呼叫時執行一次,以後再呼叫不會再次執行constructor
  4. object通常用於作為單例模式的實現,或者放class的靜態成員,比如工具方法
scala> :paste
// Entering paste mode (ctrl-D to finish)

object Person{
  private var eyeNum=2
  println("this Person object!")
  def getEyeNum = eyeNum
}

scala> Person.getEyeNum
this Person object!
res3: Int = 2

伴生物件

  1. 如果有一個class,還有一個與class同名的object,那麼就稱這個object是class的伴生物件,class是object的伴生類
  2. 伴生物件伴生類和伴生物件必須放在一個.scala檔案之中
  3. 伴生類和伴生物件,最大的特點就在於,互相可以訪問private field
scala> :paste
// Entering paste mode (ctrl-D to finish)

class Person(val name:String,val age:Int){
  def sayHello = println("Hi,"+name+",I guess you are "+age+"years old!"+Person.eyeNum)
}
object Person{
 private val eyeNum = 2
 def getEyeNum = eyeNum
}

scala> val p = new Person("TOM",30)
p: Person = [email protected]

scala> p.sayHello
Hi,TOM,I guess you are 30years old!2

讓object繼承抽象類

  1. object的功能其實和class類似,除了不能定義接收引數的constructor之外
  2. object也可以繼承抽象類,並覆蓋抽象類中的方法
scala> :paste
// Entering paste mode (ctrl-D to finish)

abstract class Hello(val message:String){
 def sayHello(name:String):Unit
}
object HelloImpl extends Hello("hello"){
 override def sayHello(name:String)={
  println(message+","+name)

}
}


scala> HelloImpl.sayHello("world")
hello,world

apply方法

  1. object中非常重要的一個特殊方法,就是apply方法
  2. 通常在伴生物件中實現apply方法,並在其中實現構造伴生類的物件的功能
  3. 而建立伴生類的物件時,通常不會使用new Class的方式,而是使用Class()的方式,隱式地呼叫伴生物件的apply方法,這樣會讓物件建立更加簡潔
  4. 比如,Array類的伴生物件的apply方法就實現了接收可變數量的引數,並建立一個Array物件的功能
  5. val a = Array(1,2,3,4,5)
scala> :paste
// Entering paste mode (ctrl-D to finish)

class Person(val name: String)
object Person{
  def apply(name:String)=new Person(name)
}

// Exiting paste mode, now interpreting.

defined class Person
defined object Person

scala> val p1 = new Person("leo")
p1: Person = [email protected]

scala> val p2 = Person("leo")
p2: Person = [email protected]

main方法

  1. 方法入口
  2. scala中main方法定義為def main(args:Array[String]),而且必須定義在object中
object HelloWorld{
  def main(args:Array[String]){
   println("Hello World!!")
  }
}
  1. 除了自己實現main方法之外,還可以繼承App Trait,然後將需要在main方法中執行的程式碼,直接作為object的constructor程式碼;而且用args可以接受傳入的引數
object HelloWorld extends App{
  if(args.length > 0) println("hello," + args(0))
  else println("Hello World!!")
}
  1. 執行上述程式碼,需要放入.scala檔案中,然後使用scalac編譯,再用scala執行class檔案 scala -Dscala.time HelloWorld
  2. App Trait的工作原理為:App Trait繼承自DelayedInit Trait,scalac命令進行編譯時,會把繼承App Trait的object的constructor程式碼都放到DelayedInit Trait的delayedInit方法中執行

用object來實現列舉功能

  1. scala沒有直接提供類似於java中的Enum這樣的列舉特性,如果要實現列舉,則需要用object繼承Enumeration類,並且呼叫Value方法來初始化列舉值
object Season extends Enumeration{
 val SPRING,SUMMER,AUTUMN,WINTER = Value
}

scala> Season.SPRING
res0: Season.Value = SPRING
  1. 還可以通過Value傳入列舉值的id和name,通過id和toString可以獲取,還可以通過id和name來查詢列舉值
scala> :paste
// Entering paste mode (ctrl-D to finish)

object Season extends Enumeration{
  val SPRING = Value(0,"spring")
  val SUMMER = Value(1,"summer")
  val AUTUMN = Value(2,"autumn")
  val WINTER = Value(3,"winter")
}

// Exiting paste mode, now interpreting.

defined object Season

scala> Season(0)
res0: Season.Value = spring

scala> Season.withName("spring")
res1: Season.Value = spring

scala> for(ele <- Season.values) println(ele)
spring
summer
autumn
winter

面向物件程式設計之繼承

  1. 讓子類繼承父類,與java一樣,也是使用extends關鍵字
  2. 繼承就代表,子類可以從父類繼承父類的field和method;然後子類可以在自己內部放入父類所沒有,子類特有的field和method;使用繼承可以有效複用程式碼
  3. 子類可以覆蓋父類的filed和method;但是如果父類用final修飾,field和method用final修飾,則該類是無法被繼承的,field和method是無法被覆蓋的
scala> :paste
// Entering paste mode (ctrl-D to finish)

class Person{
  private var name = "leo"
  def getName = name
}
class Student extends Person{
 private var score = "A"
 def getScore = score
}

// Exiting paste mode, now interpreting.

defined class Person
defined class Student

scala> val s1 = new Student()
s1: Student = [email protected]

scala> s1.getScore
res3: String = A

scala> s1.getName
res4: String = leo

override和super

  1. 如果子類要覆蓋一個父類中的非抽象方法,則必須使用override關鍵字
  2. override關鍵字可以幫組我們儘早的發現程式碼裡的錯誤,覆寫方法錯了就會報錯
  3. 在子類覆蓋父類方法之後,如果我們在子類中就是要呼叫父類的被覆蓋的方法,那就可以使用super關鍵字,顯式地指定要呼叫父類的方法
scala> :paste
// Entering paste mode (ctrl-D to finish)

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
}

// Exiting paste mode, now interpreting.

defined class Person
defined class Student

scala> val st = new Student
st: Student = [email protected]

scala> st.getName
res5: String = Hi,I'm leo

isInstanceOf和asInstanceOf

  1. 如果我們建立了子類的物件,但是又將其賦予了父類型別的變數,則在後續的程式中,我們又需要將父類型別的變數轉換為子類型別的變數
  2. 使用isInstanceOf判斷物件是否是指定類的物件,如果是的話,則可以使用asInstanceOf將物件轉換為指定型別
  3. 注意,如果物件是null,則isInstanceOf一定返回false,asInstanceOf一定返回null
  4. 注意,如果沒有用isInstanceOf先判斷物件是否為指定類的例項,就直接用asInstanceOf轉換,則可能會丟擲異常
scala> :paste
// Entering paste mode (ctrl-D to finish)

class Person
class Student extends Person
val p:Person = new Student
var s:Student = null
if(p.isInstanceOf[Student]) s = p.asInstanceOf[Student]

// Exiting paste mode, now interpreting.

defined class Person
defined class Student
p: Person = [email protected]
s: Student = [email protected]

scala> s
res9: Student = [email protected]

getClass和classOf

  1. isInstanceOf只能判斷物件是否是指定類以及其子類的物件,而不能精確判斷出,物件就是指定類的物件
  2. 如果要求精確地判斷物件就是指定類的物件,那麼就只能使用getClass和classOf了
  3. 物件.getClass可以精確獲取物件的類,classOf[類]可以精確獲取類,然後使用==操作符即可判斷
scala> :paste
class Person
class Student extends Person
val p:Person = new Student
p.isInstanceOf[Person]


// Exiting paste mode, now interpreting.

defined class Person
defined class Student
p: Person = [email protected]
res11: Boolean = true

scala> p.getClass == classOf[Person]
res12: Boolean = false

scala> p.getClass == classOf[Student]

使用模式匹配進行型別判斷

  1. 在實際開發中,比如spark的原始碼中,大量的地方使用模式匹配的方式來進行型別的判斷
  2. 功能性上來說,與isInstanceOf一樣,判斷主要是該類以及該類的子類的物件即可,不是精準判斷的
scala> :paste
// Entering paste mode (ctrl-D to finish)

class Person
class Student extends Person
val p:Person = new Student
p match{
 case per:Person => println("its Person object")
 case _ => println("unkonwn type") 
}

// Exiting paste mode, now interpreting.

its Person object
defined class Person
defined class Student
p: Person = [email protected]

protected

  1. 跟java一樣,使用protected關鍵字修飾的filed和method,在子類中就不需要super關鍵字,直接就可以訪問field和method
  2. 還可以使用protected[this],則只能在當前子類物件中訪問父類的field和method,無法通過其他子類物件訪問父類的field和mehtod
scala> :paste
// Entering paste mode (ctrl-D to finish)

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)
 }
}

// Exiting paste mode, now interpreting.

<pastie>:20: error: value hobby is not a member of Student
  println("my hobby is "+hobby + ",your hobby is " +s.hobby) 

呼叫父類的constructor

  1. 每個類可以有一個主constructor和任意多個輔助constructor,而每個輔助constructor的第一行都必須是呼叫其他輔助constructor或者是主constructor;因此子類的輔助constructor是一定不可能直接呼叫父類的constructor的
  2. 只能在子類的主constrctor中呼叫父類的constructor
scala> :paste
// Entering paste mode (ctrl-D to finish)

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)
 }
}

// Exiting paste mode, now interpreting.

defined class Person
defined class Student

匿名內部類

  1. 在scala中匿名子類非常常見,相當於java匿名內部類
  2. 定義一個類沒有名稱的子類,並直接建立其物件,然後將物件的引用賦予一個變數,之後甚至可以將該匿名子類的物件傳遞給其他函式
scala> :paste
// Entering paste mode (ctrl-D to finish)

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)
}

// Exiting paste mode, now interpreting.

defined class Person
p: Person = [email protected]
greeting: (p: Person{def sayHello: String})Unit

scala> p.sayHello
res5: String = Hi,I'm leo

scala> greeting(p)
Hi,I'm leo

抽象類

  1. 和java同樣的原理
scala> :paste
// Entering paste mode (ctrl-D to finish)

abstract class Person(val name:String){
 def sayHello:Unit
}
class Student(name:String) extends Person(name){
 def sayHello:Unit = println("Hello, "+name) //可以省略override
}

// Exiting paste mode, now interpreting.

defined class Person
defined class Student

scala> val s = new Student("jike")
s: Student = [email protected]

scala> s.sayHello
Hello, jike

抽象field

  1. 如果在父類中,定義了field,但是沒有給出初始值,則此field為抽象field
  2. 抽象filed意味著,scala會根據自己的規則,為var或val型別的field生成對應的getter和setter方法,但是父類中沒有該field的
  3. 子類必須覆蓋field,以定義自己的具體field,並且覆蓋抽象field,不需要使用override關鍵字
abstract class Person{
 val name :String
}

class Student extends Person{
 val name :String = "leo"
}

scala> val s = new Student
s: Student = [email protected]

scala> s.name
res9: String = leo