Scala(二)-- 類、物件、繼承、特質
1.類
1)在scala中,類並不用宣告為public
如果沒有定義構造器,類會有一個預設的無參構造器 var修飾的變數,對外提供getter setter方法 val修飾的變臉,提供getter方法,沒有setter方法 var name:String = _ _表示一個佔位符,編譯器會根據你變數的具體型別賦予相應的初始值 使用佔位符,變數型別必須指定,val修飾的變數不能使用佔位符
class StuDemo { //_是佔位符,使用時需要屬性的型別資訊 var name:String = _ //val使用前必須初始化,修飾的變數不能使用佔位符 //除了介面或者抽象類,裡面的屬性都需要初始化 val age:Int = 10 //類私有欄位,當前類的其它方法或者類的伴生物件可以訪問 private var hobby:String = "traveing" //生成get set方法,但方法是私有的,本類的方法可以訪問 //物件私有欄位--只有在當前物件方法可以呼叫 private [this] val cardinfo = "123456" //沒有get set方法 def compare(obj:StuDemo)={ if(this.age != obj.age) this.age-obj.age //else if(this.cardinfo != obj.cardinfo) } } object TestStu{ def main(args: Array[String]): Unit = { //如果不定義構造方法,有一個無參的構造方法 val obj = new StuDemo println(obj.age) println(obj.name) } }
2)手動建立變數的getter和setter方法需要遵循以下原則:
(1) 欄位屬性名以“_”作為字首,如:_x (2) get方法定義為:def x = x (3) set方法定義時,方法名為屬性名去掉字首,並加上字尾,字尾是:“def x=”
//欄位屬性名以“_”開始 class Point { private var _x = 0 private var _y = 0 private val bound = 100 //get方法,方法名是屬性名去掉“_” def x = _x //set方法,方法名是屬性名去掉“_”,在後面加一個“_” def x_=(newValue:Int) = { if (newValue<bound)_x = newValue else println("nothing") } def y = _y def y_= (newValue:Int)={ if (newValue < bound)_y =newValue else println("out of bound") } } object testPoint{ def main(args: Array[String]): Unit = { val pointObj = new Point() println(pointObj.x) pointObj.x_=(200) pointObj.y_=(150) } }
3)Scala中定義類的構造方法
scala中構造分為主構造器和輔助構造器 (1)主構造的引數直接放置於類名之後 (2)主構造器會執行類定義中的所有語句 (3)通過private設定的主構造器的私有屬性 (4)輔助構造器名稱為this,(def this(…))通過不同引數進行區分,第一句一定是呼叫主構造方法或者其他的輔助構造方法
//主構造方法的引數直接放在類名之後 class ClassConstructor private (var name:String,val price:Double) { //私有時直接 ClassConstructor private...私有構造方法不能直接呼叫構造物件 println(name + "," + price) //會執行類中的所有語句 private var weight:Double = 0 private var color:String = _ def myPrintln = println(name + "," + price) //輔助構造方法,this,第一句一定是呼叫主構造方法或者其他的輔助構造方法 def this(name:String,price:Double,weight:Double){ this(name,price) this.weight = weight } def this(name:String,price:Double,weight:Double,color:String){ this(name,price,weight) this.color = color } } object testConstructor{ def main(args: Array[String]): Unit = { val obj = new ClassConstructor("apple",20.0,100.0) //obj.myPrintln } }
2.特質
特質的定義除了使用關鍵字trait之外,與類定義無異
介面中可以: 宣告一個有值的欄位、宣告一個抽象欄位、宣告一個=帶有具體實現的方法、宣告一個抽象方法 在triat中可以定義抽象方法,就與抽象類中的抽象方法一樣,只要不給出方法的具體實現即可 類可以使用extends關鍵字繼承trait,注意,這裡不是implement,而是extends,在scala中沒有implement的概念,無論繼承類還是trait,統一都是extends
//特質(介面)
trait Flyable{
//介面中可以定義
//宣告一個有值的欄位
//介面中的欄位:類中繼承了這個欄位,介面中的欄位會直接新增到實現了(繼承了)該介面的類中
//普通類中的欄位:如果是普通的類,父類中的欄位在子類中會有一個引用地址,指向父類的屬性
val distant:Int = 100
//宣告一個抽象欄位
val height:Int
//宣告一個=帶有具體實現的方法
def fly:String = {
"I can fly"
}
//宣告一個抽象方法
def fight:String
}
trait Logger{
def log(msg:String)
}
class ConsoleLogger extends Logger{
//類中實現抽象方法,重寫抽象方法可以不用override
def log(msg: String): Unit = println(msg)
}
class Account{
var balance = 0.0
}
//一個以上的特質用with
class SavingAccount extends Account with Logger{
//override def log(msg: String): Unit = println("SavingAccount")
override def log(msg: String): Unit = println(msg)
def withdraw(amount:Double):Unit = {
if(amount > balance) log("餘額不足")
else balance -= amount
}
}
//單繼承,可以實現多介面
object TraitDemo {
def main(args: Array[String]): Unit = {
// val obj = new ConsoleLogger
// obj.log("trait test")
val obj = new SavingAccount
obj.withdraw(100)
}
}
類繼承trait後,必須實現其中的抽象方法,實現時不需要使用override關鍵字 scala不支援對類進行多繼承,但是支援多重繼承trait,使用with關鍵字即可
特質繼承類(特別點) 在Scala中,trait也可以繼承自class,此時這個class就會成為所有繼承該trait的類的父類
//特質繼承類
class MyUtil{
def printmsg(msg:String) = println(msg)
}
trait MyTrait extends MyUtil{
def log (msg:String) = printmsg(msg)
}
class Demos(val name:String) extends MyTrait{
def sayHello:Unit = {
log(name+"0000000")
printmsg(name+"111111")
}
}
object TraitDemo2 {
def main(args: Array[String]): Unit = {
val obj = new Demos("zhangfasd")
obj.sayHello
}
}
3.抽象類
定義一個抽象類:abstract 如果某個類至少存在一個抽象方法或一個抽象欄位,則該類必須宣告為abstract。
abstract class Person{
//抽象欄位,有初值的欄位,抽象方法:沒有方法體,是抽象方法,具體實現的方法、
val age = 20
var name:String
def id:Int //沒有初始值,抽象欄位
}
class Employ extends Person{
//實現抽象欄位或者抽象方法
var name:String = "tom"
def id = name.hashCode
}
//object
object AbstactclassDemo {
def main(args: Array[String]): Unit = {
val obj = new Employ
println(obj.id)
}
}
4、物件
1)單例物件
在Scala中沒有靜態方法和靜態欄位,但是可以使用object這個語法結構來達到同樣的目的
object ObjectTest {
def main(args: Array[String]): Unit = {
val s = SessionFactory
println(s.getSession())
println(s.getSession().size)
println(s.removeSession)
println(s.getSession().size)
}
}
class Session{
}
object SessionFactory{
print("SessionFactory被執行")
//計數器
var i = 5
//存放seesion物件
val sessions = new ArrayBuffer[Session]()
//session新增新的session
while(i>0){
sessions.append(new Session)
i -= 1
}
//獲取session物件
def getSession() = sessions
//刪除session物件
def removeSession:Unit = {
val session = sessions(0)
sessions.remove(0)
println("被刪除的session物件"+session)
}
}
類和單例物件的區別是:
單例物件不能帶引數,單例物件不能用new關鍵字例項化,所以沒有機會傳遞給它例項化的引數。 單例物件在第一次訪問的時候才會初始化。 當單例物件與某個類同名時,它被稱為類的伴生物件,類和伴生物件必須定義在一個原始檔裡,類稱為該單例物件的伴生類,類和他的伴生物件可以互相訪問其私有成員。 不與伴生類共享名稱的單例物件被稱為獨立物件,可以作為相關功能的工具類,或者scala應用程式的入口點。
2)伴生物件
在Scala的類中,與類名相同並且用object修飾的物件叫做伴生物件,類和伴生物件之間可以相互訪問私有的方法和屬性,他們必須存在同一個原始檔中
//伴生物件要求:名字和類名是一致的,在一個原始檔裡
//優勢:類和伴生物件之間可以互相訪問彼此的私有屬性或者方法
class AccountInfo{
//這裡可以定義私有方法
var id = AccountInfo.newUniqueNumber()
private var balance = 0
}
object AccountInfo{
private var lastNumber = 0
private def newUniqueNumber()={
// val obj =new AccountInfo
// println(obj.balance)
lastNumber += 1//賦值語句的返回值是() Unit
println("lastNumber:"+lastNumber)
lastNumber//返回值
}
}
object ObjectTest2 {
def main(args: Array[String]): Unit = {
val obj = new AccountInfo
obj.id
}
}
對伴生物件的理解(單例物件) 伴生物件要求:名字和類名是一致的,在一個原始檔裡 優勢:類和伴生物件之間可以互相訪問彼此的私有屬性或者方法,private this 修飾除外
3)應用程式物件
Scala程式都必須從一個物件的main方法開始,可以通過擴充套件App特質,不寫main方法。
object Hello extends App{
println("Hello World")
}
5.unapply 和apply方法
unapply 和apply方法的區別,以及使用場景:
apply unapply 都在伴生方法中被定義,都是系統會隱式呼叫的 apply方法(注入):建立類的物件,不使用new關鍵字時被系統隱式呼叫,是建立物件 的工廠 unapply方法(提取):提取物件 的屬性資訊時,一般被用於模式匹配或者偏函式中
apply:
object AccountInfo1{
private var lastNunber = 0
//apply方法
def apply(a:Int)={ //被系統隱式呼叫,使用類名,沒有new,會自動使用apply方法:var arr = Array(1,2,3,4,5)
println("apply 方法被呼叫")
lastNunber = a+1
lastNunber
}
}
object ObjectTest3 {
def main(args: Array[String]): Unit = {
println(AccountInfo1(1))
println(AccountInfo1.apply(2))
}
}
unapply:
object CustomID{
def apply(name:String) = s"$name---${Random.nextLong()}" // 差值器
//some() none
def unapply(CustomID:String): Option[String] = {
println("unapply方法被呼叫")
val name = CustomID.split("---").head
if(name.nonEmpty) Some(name) else None
}
}
object ObjectTest4 {
def main(args: Array[String]): Unit = {
val custoobj = CustomID("jerry")
custoobj match {
case CustomID(name) => println(name)
//相當於java中的default
case _ => println("not match")
}
}
}
6.繼承
Scala中,讓子類繼承父類,與Java一樣,也是使用extends關鍵字 繼承就代表,子類可以從父類繼承父類的屬性和方法;然後子類可以在自己內部放入父類所沒有,子類特有的屬性和方法;使用繼承可以有效複用程式碼 子類可以覆蓋父類的屬性和方法;但是如果父類用final修飾,則該類是無法被繼承的,屬性和方法用final修飾,屬性和方法是無法被覆蓋的
class Person1{
private var name = "tom"
val age = 0
def getName = name
}
class Student extends Person1{
private var score = 100
def getScore = score
//super呼叫父類的同名方法
//override重寫父類的非抽象方法,非抽象欄位
override val age = 0
override def getName: String = super.getName + " " +getScore
}
object ExtendsTest1 {
def main(args: Array[String]): Unit = {
val s = new Student
println(s.getScore)
println(s.getName)
}
//val per = new Person1
var per:Person1 = new Student
//
var student:Student = null
if(per.isInstanceOf[Student])
student=per.asInstanceOf[Student]
println(student.getScore)
}
注意: (1)Override和super Scala中,如果子類要覆蓋一個父類中的非抽象方法,則必須使用override關鍵字,override還可以覆蓋變數 此外,在子類覆蓋父類方法之後,如果我們在子類中就是要呼叫父類的被覆蓋的方法呢?那就可以使用super關鍵字,顯式地指定要呼叫父類的方法
(2)父類子類轉換 isInstanceOf和asInstanceOf
if(per.isInstanceOf[Stus])
stus = per.asInstanceOf[Stus]
println(stus)