您好,登錄后才能下訂單哦!
這篇文章主要講解了“Scala中面向對象編程怎么用”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Scala中面向對象編程怎么用”吧!
scala在數據的處理上使用的是函數式編程思想,但在上層的架構組織中仍使用面向對象的模型,對于大型應用程序尤其重要。
?
類的聲明使用關鍵字 class:
class Counter{ //這里定義類的字段和方法}
Scala中建議類名首字母大寫。
要定義類的字段,使用var/val,和變量的定義一樣。
要定義類的方法,使用def 關鍵字。
下面來定義一個完整的類:
class Woman{ //定義類的字段val sex = "female"//定義類的方法def usedToBeGirl():Unit = { println("A Woman used to be a girl.")}}
方法usedToBeGirl()
的返回值類型為Unit
,代表它沒有返回任何結果,是一個空值,表示為()
。
scala的類允許嵌套定義
方法的調用
方法若沒有參數,在定義時可以省略括號。
方法定義時若省略了括號,那么調用時也不能帶括號;若沒有省略括號,則調用時帶不帶括號都可以。
比如:
class MyFather{ //定義類的字段val age = 40val sex = "male"//定義類的方法def getAge():Int = agedef getSex:String = sex}
這里getAge
方法的調用:
//先創建一個實例val f = new MyFather//以下這兩種調用方法都可以f.getAge f.getAge()//getSex方法聲明時沒有括號,調用時也不能有括號f.getSex
?
方法的返回值
方法有聲明返回值類型,那么返回值是方法體中最后一條執行語句的值;
方法沒有聲明返回值類型,那么scala編譯器會根據方法體中最后一條執行語句的值來推斷返回值類型。
此時如果省略了等號,使用大括號,scala編譯器會將返回值推斷為Unit。
方法參數
Scala中,方法的參數都是不可變的,隱式使用了 val 進行聲明,所以既不可以在聲明參數時使用var 或val ,也不可以在方法體中重新給參數賦值。
在scala中調用方法時可以顯式地命名參數列表。
scala中允許方法重載
scala中允許方法的嵌套定義
scala類中,所有成員的默認可見性都是公有的,且不需要使用 public 關鍵字進行限定。
除了public之外,scala也具有和java同樣的可見性選項:private和protected。
private:對本類和嵌套類可見
protected:對本類和子類可見
scala是不推薦將所有成員設置為公有屬性的,而建議將其設置為private,這樣外部成員就無法直接訪問私有字段。因此,像java類中使用的getter和setter方法一樣,scala也提供對private 字段進行訪問和修改的方法。
class Counter{ //定義一個private字段private var privateValue = 0//提供訪問和修改privateValue的方法def value = privateValuedef value_ = (newValue:Int){ if (newValue > 0) privateValue = newValue} }
其中,value 和value_ 這兩個方法是成對的。
訪問privateValue字段:
//先實例化一個對象val counter = new Counter//訪問privateValuecouner.value//修改privateValuecounter.value_(3)
上面對私有字段進行修改的方法也可以省略下劃線_(scala語法規定):
counter.value(3)
這樣會顯得更加直觀。
在scala中,整個類的定義主體就是類的構造器,稱為主構造器,所有位于類方法以外的語句都將在構造過程中被執行。
那么如何向構造器傳入參數呢?
scala規定在類名之后使用圓括號列出主構造器的參數列表:
class Counter(name:String){ private val value = 0}
除了主構造器,scala類還可以擁有零個或多個輔助構造器。
每個輔助構造器的第一條語句必須是對主構造器或者此前已經定義的輔助構造器的調用。
輔助構造器使用this進行定義,調用形式為:this(參數列表)
。
與普通方法的定義不同,構造器的參數可以使用val 或var進行定義。scala將自動為這些參數創建私有字段。
使用val 定義的參數,只會為其生成讀方法,因為它是不可變的,只可以讀不可以寫。
scala中的單例對象相當于java中的靜態成員。
單例對象的定義使用object 關鍵字:
object Person{ private var lastId = 0{ lastId += 1 lastId }}
單例對象在第一次被訪問的時候初始化。
單例對象包括兩種:伴生對象和孤立對象。
當一個單例對象具有同名類,則這個單例對象就稱為這個同名類的伴生對象。相應的,這個同名類就稱為這個單例對象的伴生類。它們之間可以互相訪問對方的私有成員。
反之,沒有同名類的單例對象,就稱為孤立對象。比如scala程序的入口main方法就定義在一個孤立對象里:
object HelloWorld{ //HelloWorld是一個孤立對象def main(args[]:Array[String]){ println("Hello World!")}}
對于包含了伴生類和伴生對象定義的代碼文件,不能直接在REPL中使用":load"命令來執行,需要先對其進行編譯,再用scala命令來執行。
之前的介紹中曾經使用過apply方法:
val strArr = Array("hadoop","spark","flink")
可以看到這里實例化了一個數組對象strArr,但是并沒有使用new關鍵字進行創建,這里實際上就是隱式調用了apply方法。整個過程就是,scala自動調用了Array類的伴生對象Array中一個apply方法,創建了一個Array對象。
apply方法也可以定義在類中,比如說定義如下類TestApply:
class TestApply{ def apply(param:String):Unit = { println("apply method called " + param)}}
在REPL模式下執行程序:
先創建一個TestApply對象,名為myObject。然后執行myObject("Hello Apply")
,傳遞了一個參數,執行的結果就是類中的apply方法被自動調用了。
scala> val myObject = new TestApply val myObject: TestApply = TestApply@3caf5c96scala> myObject("Hello Apply")apply method called Hello Apply
然而我的學識尚淺,有一個疑惑,把apply方法名改成別的還會自動調用該方法嗎?
經過我的實驗可以證明,答案是:不會。
將TestApply類中的apply方法改名為helloApply,然后重新執行程序:
scala> :load /usr/local/scala/mycode/testApply.scalaval args: Array[String] = Array()Loading /usr/local/scala/mycode/testApply.scala...class TestApply scala> val myObject = new TestApplyval myObject: TestApply = TestApply@53da2aecscala> myObject("Hello Apply") ^ error: TestApply does not take parameters scala> myObject.helloApply("Hello Apply")apply method called Hello Apply只有顯式調用helloApply方法才行。
apply方法定義在類的伴生對象中(更通常的用法)
將類的構造方法以apply方法的形式定義在類的伴生對象中,類的伴生對象就成為了一個“工廠”,專門生產類的實例,apply方法就稱為工廠方法。
實際上,apply方法設計的初衷就是為了保持對象和函數之間使用的一致性,數學中函數的使用形式是函數(參數)
,在scala中函數也是對象,因此scala的方法調用可以省去對方法的調用,像使用函數一樣使用方法。
函數(參數)函數.方法(參數)
在scala中,apply的調用規則是:用括號傳遞給類實例或對象名一個或多個參數時,scala會在相應的類或對象中查找名為apply的方法,且要求參數列表與傳入參數一致,然后調用該apply方法。
update 方法
update跟apply方法遵循同樣的調用規則,update方法用于對象的重新賦值,比如可變Map類的伴生對象中就有update方法,可以修改某個鍵的值。
?
unupply方法用于對對象進行解構操作,它也會被自動調用。
unapply方法可以看做apply方法的反向操作,它從某個類的構造函數中解構出該函數的傳入參數。
若一個類包含沒有實現的成員(字段或方法),那么它就是抽象類,要使用abstract 關鍵詞對該類進行修飾。
abstract class Person{ val name:Stringval age:Intdef getAge()def greeting(){ println("hello")}}
抽象類中的抽象字段必須聲明類型。抽象類不能實例化,只能作為父類被其它類繼承。
與java不同的是,scala抽象類中的抽象方法不需要加abstract修飾。
scala中的繼承與java一樣,只支持單一繼承,即一個子類只能有一個父類。繼承父類使用關鍵字使用extends關鍵字表示。
定義子類時,需要注意以下幾點:
重載父類的抽象成員時,override關鍵字是可選的;重載父類的非抽象成員時,override關鍵字是必選的。
建議重載父類的抽象成員也使用關鍵字override,若在后續的業務中父類的抽象成員被實現了,此時該成員就是非抽象成員。因為重載非抽象成員時override關鍵字是必選的,如果子類沒有使用override關鍵字,就會出現編譯錯誤,此時用戶會及時發現父類的改變。
子類在重載時只能重載val修飾的字段,var本身就是可變的,重載并沒有意義。
子類的主構造器必須調用父類的主構造器或輔助構造器。
若父類構造器中有val或var修飾的參數,它就相當于類的字段,因此如果子類的構造器參數中包含與父類構造器中同名的參數,那么子類構造器相當于重載了父類字段,需要使用override對該構造器參數進行修飾。
類型 | 描述 |
---|---|
Null | 所有引用類型的子類。值為null,表示一個空對象。 |
Nothing | Nothing類型在Scala的類層級的最底端;它是任何其他類型的子類型。 |
Any | Any是所有其他類的超類 |
AnyRef | AnyRef類是Scala里所有引用類(reference class)的基類 |
Nothing主要用于異常處理函數的返回值類型,這樣異常拋出后不會返回任何對象,所以異常處理函數可以更為方便地使用。
scala采用Option類來統一表示對象有值和無值的情況,Option是一個抽象類,有一個具體的類Some和一個對象Node。Some表示有值,None表示無值。
scala中的參數化類型相當于java中的泛型,與之不同的是scala中使用方括號[] 來定義參數化類型。
scala中的特質Trait 相當于java中的接口。一個特質被子類實現以后,就可以看做這個類的父類,但與繼承不同的是,特質允許多重實現,也就是一個子類可以混入多個特質。
特質可以同時用擁有具體方法和抽象方法,這就跟抽象類很相似。
注:
如果特質沒有顯式地說明繼承關系,那么這個特質就默認繼承自AnyRef。
如果特質繼承自某個父類,那么它無法向該父類構造器傳遞參數,這就要求特質所繼承的父類必須有一個無參構造器。
若要混入多個特質,可以使用with關鍵字(可以連續使用):
class Man extends Person with Boy{ //字段與方法實現}
特質不能實例化
match 語句
scala中最常見的模式匹配是match語句,類似于其它語言中的switch語句。
val grade = 90grade match { case 'A' => println("85-100")case 'B' => println("70-84")case 'C' => println("60-69")case _ => println("unqualified!")}
最后一個case使用了下劃線"_"代表其它情況,相當于java中的default分支。
case 類
定義一個類時,如果在class關鍵字前加上case修飾,則該類為case類。scala為case類重載了許多實用的方法,包括tiString,equals和hashcode方法。更重要的是,scala為每一個case類自動生成一個伴生對象。
為了解決程序中的命名沖突,scala也和java一樣使用包來層次化、模塊化地組織程序。
將代碼放在指定包中的兩種方法:
在代碼文件的頂端使用package關鍵字聲明
在package子句中加一對大括號,將類和對象放在大括號里
感謝各位的閱讀,以上就是“Scala中面向對象編程怎么用”的內容了,經過本文的學習后,相信大家對Scala中面向對象編程怎么用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。