1. 程式人生 > >Scala(二)-- 類、物件、繼承、特質

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)