重游scala04
Scala編程
?
?
| 課程大綱 | 課程內容 | 學習效果 | 掌握目標 |
| Scala編程數據結構 | Scala類、對象 | 掌握 | 熟練掌握類的定義,創建對象 熟練掌握繼承和特質 熟練掌握樣例類、模式匹配 ? |
| Scala繼承 | 掌握 | ||
| Scala特質 | 掌握 | ||
| Scala樣例類 | 掌握 | ||
| Scala模式匹配 | 掌握 |
?
?
Scala的類與Java、C++的類比起來更簡潔,學完之后你會更愛Scala!!!
一、類
1、類的定義
Scala 訪問修飾符有:private,protected,public。
| //在Scala中,類的定義可以通過class關鍵字實現,類并不用聲明為public。 //Scala源文件中可以包含多個類,所有這些類都具有公有可見性。 class Dog { ??//屬性必須有初始值,如果不想有初始值初始化為_下劃線,賦值為_是必須指定數據類型 ??private var?leg = 4 ??def shout(content: String) { ????println(content) ??} ??def currentLeg = leg } |
?
使用這個類:
| //在Scala中,類并不用聲明為public。默認就是public val dog = new Dog //調用方法,通過對象.方法 對象+空格+方法 dog shout "汪汪汪" dog.shout("汪汪汪") println(dog currentLeg) println(dog.currentLeg) //訪問屬性,通過對象.屬性 對象+空格+屬性 dog.leg = 4 println(“leg = “+dog.leg) dog leg = 4 println(dog leg) |
?
| //在Scala中,類并不用聲明為public。 //Scala源文件中可以包含多個類,所有這些類都具有公有可見性。 class Person { ??//用val修飾的變量是只讀屬性,有getter但沒有setter ??//(字面量,相當與Java中用final修飾的變量) ??val id?= "9527" ??//用var修飾的變量既有getter又有setter ??var age: Int = 18 // _代表初始值,使用時屬性必須指定類型 var color:Strng =?_ ??//類私有字段,只能在伴生對象和類的內部使用 ??private var name: String = "唐伯虎" ??//對象私有字段,訪問權限更加嚴格的,當前對象的字段只能被Person類的方法訪問到 //對象私有字段,只能在類的內部使用 } |
?
尖叫提示:在Scala中,類并不聲明為Public,一個Scala源文件可以包含多個類。所有這些類都具有公有可見性。調用無參方法時,可以加(),也可以不加;如果方法定義時不帶括號,那么調用時就不能帶括號。
2、Getter?Setter方法
對于scala類中的每一個屬性,編譯后,會有一個私有的字段和相應的getter、setter方法生成:
| //getter println(dog leg) //setter //對象.屬性_=(值) dog.leg_=(10) println(dog currentLeg) |
當然了,你也可以不使用自動生成的方式,自己定義getter和setter方法
| class Dog2 { ??private var _leg = 4 ??def leg = _leg ??def leg_=(newLeg: Int) { ????_leg = newLeg ??} } |
使用之:
| val dog2 = new Dog2 dog2.leg_=(10) println(dog2.leg) |
?
尖叫提示:自己手動創建變量的getter和setter方法需要遵循以下原則:
1) 字段屬性名以“_”作為前綴,如:_leg
2)?getter方法定義為:def leg = _leg
3) setter方法定義時,方法名為屬性名去掉前綴,并加上后綴,后綴是:“leg_=”,如例子所示
?
3、Bean屬性
JavaBeans規范定義了Java的屬性是像getXXX()和setXXX()的方法。許多Java工具都依賴這個命名習慣。為了Java的互操作性。將Scala字段加@BeanProperty時,這樣的方法會自動生成。
創建一個Bean,使用@BeanProperty注解標識某個屬性變量
| import scala.beans.BeanProperty class Person { ??@BeanProperty var name: String = _ } |
使用@BeanProperty注解標識的屬性只能以字母開頭,不能下劃線開頭。
?
通過getName、setName訪問屬性
| val person = new Person person.setName("Nick") person.getName println(person.name) |
尖叫提示:
Person將會生成四個方法:
--name:String
--name_=(newValue:String): Unit
--getName():String
--setName(newValue:String):Unit
?
?
4、構造器
scala中構造分為主構造器和輔助構造器
注意:主構造器會執行類定義中的所有語句
| /** ??*每個類都有主構造器,主構造器的參數直接放置類名后面,與類交織在一起 ??*/ class Student(val name: String, val age: Int){ ??//主構造器會執行類定義中的所有語句 ??println("執行主構造器") ? ??private var gender = "male" this(name, age) } ? //主構造器中name屬性沒有初始值,age屬性有初始值,那么在調用主構造創建對象是,可以不傳age這個參數的值 class?Teacher(var name:String,var age:Int=30) { //如果在主構造器中屬性有初始值,那么在調用主構造創建對象是,可以不傳這個參數的值 val?t01 = new Teacher("xiaoming") ? ? |
尖叫提示:
(1)如果在主構造器中定義的參數不帶val或var,這定義的參數不是類的屬性,不能通過“對象.參數名稱”?調用;
(2)如果在主構造器中定義的參帶val或var,這定義的參數提升為類的屬性,能通過“對象.參數名稱”?調用;
(3)如果構造器參數不帶val或var,但是參數被一個方法所使用,那么它將會被提升為類的屬性。
?
?
Scala 中的 private 限定符,比 Java 更嚴格,在嵌套類情況下,外層類甚至不能訪問被嵌套類的私有成員。
用 private 關鍵字修飾,帶有此標記的成員僅在包含了成員定義的類或對象內部可見,同樣的規則還適用內部類。
?
?
?
- 對象
1、對象
Object定義的結構中的屬性和方法,是靜態的。存放工具方法和常量,即靜態的方法和屬性。
?
| object Constants{ |
2、伴生對象
在Scala的類中,與類名相同的對象叫做伴生對象。利用伴生對象,可以實現單例。
| //類A后面加了private,那么主構造器就變為私有了,外部不能new實例 ? //以下代碼實現單例 |
?
伴生類和伴生對象之間可以相互訪問私有的方法和屬性。也就是說,伴生類和伴生對象之間不受private限制。
| package cn.bigdata.scala ? |
3、apply方法
通常我們會在類的伴生對象中定義apply方法。
| object Teacher { ? |
?
?
?
一般,在伴生對象中會寫apply()方法,用于創建伴生類的實例。
當遇到類名(參數1,...參數n)時apply方法會被調用。
| package cn.bigdata.scala
??def main(args: Array[String]) { //調用了Array伴生對象的apply方法 //def apply(x: Int, xs: Int*): Array[Int] //arr1中只有一個元素5 ? class?Dog(name:String) { //通過一個參數的apply創建對象,Dog會變成斜體 //通過兩個參數的apply創建對象 ? class Cat(var name:String, var age:Int) { |
?
4、應用程序對象
Scala程序都必須從一個對象的main方法開始,可以通過擴展App特質,不寫main方法。
?
| package cn.bigdata.scala object AppObjectDemo extends App{ ? |
- 繼承
1、繼承類
在Scala中擴展類的方式和Java一樣都是使用extends關鍵字
scala中不支持多繼承。
?
和Java一樣使用extends關鍵字,在定義中給出子類需要而超類沒有的字段和方法,或者重寫超類的方法。
| ? class?Person ?{ ??var?name = "" } ? class?Employee extends?Person{ ??var?salary = 0.0 ??def?description = "員工姓名:" + name + " 薪水:" + salary } |
尖叫提示:如果類聲明為final,他將不能被繼承。如果單個方法聲明為final,將不能被重寫。
2、類型檢查和轉換
| Scala | Java | 含義 |
| obj.isInstanceOf[C] | obj instanceof C | 判斷obj是不是C類型,返回boolean的值 |
| obj.asInstanceOf[C] | (C)obj | 將obj強制轉換成C類型,返回類實例 |
| classOf[C] | C.class | 返回class |
?
| ?val a1 = new A1 ? //返回class day031$A1 |
?
3、重寫方法
在Scala中重寫一個非抽象的方法必須使用override修飾符,調用超類的方法使用super關鍵字
| class?Person { |
?
?
4、超類的構造
輔助構造器會調用主構造器,主構造器可以調用超類的構造器。
| class?Person(val name: String, val age: Int) { |
輔助構造器永遠都不可能直接調用超類的構造器,所以你不能調用super(params)。因為supre用在輔助構造器中。
5、重名字段
子類改寫父類或者抽象父類的字段,通過以下方式:
| ??class A{ |
尖叫提示:
1、val屬性只能重寫另一個val屬性或無參的def同名方法
2、var不能重寫var屬性,但能重寫抽象的var屬性
6、抽象類
可以通過abstract關鍵字標記不能被實例化的類。
在抽象類中,沒有方法體的方法是抽象方法。沒有初始值的屬性是抽象屬性。
| abstract class A{ ? |
?
1)不允許多重繼承
所有的面向對象的語言都不允許直接的多重繼承,因為會出現“deadly diamond of death”問題。Scala提供了trait(特質),特質可以同時擁有抽象方法和具體方法,一個類可以實現多個特質。
2)當做接口使用的特質
特質中沒有實現的方法就是抽象方法。類通過extends繼承特質,通過with可以繼承多個特質。
| trait?Logger { |
Logger with Cloneable with Serializable是一個整體,extends這個整體
所有的java接口都可以當做Scala特質使用。
3)帶有具體實現的特質
特質中的方法并不一定是抽象的:
| trait?ConsoleLogger { |
?
?
?
| //這是一個接口 trait?Animal { //定義一個抽象方法 //定義一個實現的方法,普通方法 ? //定義一個類去繼承接口,使用關鍵字extends class?Emp extends Animal{ ??//實現它的抽象方法,關鍵字override 可有可無 ??//重寫已經實現的方法,關鍵字override 必須有 ??override def eat(): Unit = { ? // extends 繼承一個類/抽象類 ?with 實現接口 |
?
在創建類的實例時,可以指定某個實例混入特質。那么,只有這個實例有特質,其他實例沒有。
| trait Logger{ |
?
如果類繼承多個特質,那么特質中的方法會依次執行,類似于責任者模式。
需要讓特質中的方法最后調用super。
| trait Logger{ |
?
?
?
四、模式匹配和樣例類
Scala有一個十分強大的模式匹配機制,可以應用到很多場合:如switch?case語句、類型檢查等。Java中的switch是按照類型判斷的,這里的模式匹配則可以不限制類型,功能更加強大。
1、匹配值
與default等效的是捕獲所有的case_ 模式。如果沒有模式匹配,拋出MatchError,每個case中,不用break語句??梢栽趍atch中使用任何類型可以是字符串也可以是數字,而不僅僅是數字。
| package cn.bigdata.cases ??//匹配到case 后面的值,就執行=> 后面的內容 ??name match { |
尖叫提示:如果把case _作為第一個case,則永遠匹配 case _
??????????如果沒有case_ ,且match中沒有匹配的case,則會拋出異常,scala.MatchError
| //模式匹配結果作為函數返回值 |
?
變量匹配,匹的是case語句后面接的是scala變量,如case x if(x == 5) => x等,在使用時一般會加守衛條件,當然也可以像case x => x這樣使用,它會匹配任何輸入的合法變量。
| //模式匹配結果作為函數返回值 ? |
?
它可以匹配輸入待匹配變量的類型。
| object TypePattern{ def main(args:Array[String]) :Unit = { def typePattern(t : Any) = t match {{ case t : String => "String" case t : Int => "Intger" case t : Double => "Double" case _ => "Other Type" } println(typePattern(5.0)) println(typePattern(5)) println(typePattern("5")) println(typePattern(List())) } } |
4、匹配數組、列表、元組
| package cn.bigdata.cases //_ 表示一個元素的通配符 //_*?表示0個或者多個元素的通配符 case?_ ::Nil => println("List 只有一個元素的List,這個元素是任意值") //元組只能匹配內容,固定長度的元組只能匹配對應長度的元組,只是配置內容,不能匹配長度 ? |
注意:在Scala中列表要么為空(Nil表示空列表)要么是一個head元素加上一個tail列表。
9 :: List(5, 2) ?:: 操作符是將給定的頭和尾創建一個新的列表
注意::: 操作符是右結合的,如9 :: 5 :: 2 :: Nil相當于 9 :: (5 :: (2 :: Nil))
五、異常
當碰到異常情況時,方法拋出一個異常,終止方法本身的執行,異常傳遞到其調用者,調用者可以處理該異常,也可以升級到它的調用者。運行系統會一直這樣升級異常,直到有調用者能處理它。 如果一直沒有處理,則終止整個程序。
Scala的異常的工作機制和Java一樣,但是Scala沒有“checked”異常,你不需要聲明說函數或者方法可能會拋出某種異常。受檢異常在編譯器被檢查,java必須聲明方法所會拋出的異常類型。
拋出異常:用throw關鍵字,拋出一個異常對象。所有異常都是Throwable的子類型。throw表達式是有類型的,就是Nothing,因為Nothing是所有類型的子類型,所以throw表達式可以用在需要類型的地方。
捕捉異常:在Scala里,借用了模式匹配的思想來做異常的匹配,因此,在catch的代碼里,是一系列case字句。
異常捕捉的機制與其他語言中一樣,如果有異常發生,catch字句是按次序捕捉的。因此,在catch字句中,越具體的異常越要靠前,越普遍的異常越靠后。 如果拋出的異常不在catch字句中,該異常則無法處理,會被升級到調用者處。
finally字句用于執行不管是正常處理還是有異常發生時都需要執行的步驟,一般用于對象的清理工作。
| object?ExceptionSyllabus { |
?
?
六、樣例類
在Scala中樣例類是一中特殊的類case class,默認實現了Serializable。case class是多例的,后面要跟構造參數,case object是單例的。
?
當你聲明了一個 case class,Scala 編譯器為你做了這些:
?
| case class A(id:Int, name:String, age:Int); |
?
Case class可以用于模式匹配。
| //定義樣例類 |
?
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
- 上一篇: 使用cloudera manager安装
- 下一篇: 彻底搞清楚map和flatmap