scala-類和對(duì)象

類和對(duì)象

Scala是一個(gè)函數(shù)式面向?qū)ο笳Z言

什么是面向?qū)ο螅?/p>

面向?qū)ο笫且环N變成語言,它是基于面向過程的,強(qiáng)調(diào)的是以對(duì)象為基礎(chǔ)完成各種操作

三大特點(diǎn):

  • 跟符合程序員的思考習(xí)慣
  • 把復(fù)雜的事情簡單化
  • 把程序員從執(zhí)行之變?yōu)橹笓]者

什么是類?

類是屬性和行為的集合,是一個(gè)抽象的概念,看不見也摸不著

類和無參構(gòu)造器

在Scala中,類并不用聲明為public;Scala源文件中可以包含多個(gè)類,所有這些類都具有公有可見性;val修飾的變量(常量),值不能改變,只提供getter方法,沒有setter方法;var修飾的變量,值可以改變,對(duì)外提供getter、setter方法;如果沒有定義構(gòu)造器,類會(huì)有一個(gè)默認(rèn)的無參構(gòu)造器;

  • 如果類是空的,沒有任何成員,可以省略{}
  • 如果構(gòu)造器的參數(shù)為空,可以省略()
package hhb.cn.part04

/**
 * @description:
 * 在Scala中,類都有一個(gè)無參構(gòu)造器
 * @date: 2020-09-24 10:05
 **/
class Person {
  //聲明字段必須進(jìn)行初始化,Scala編譯器會(huì)根據(jù)初始化值的數(shù)據(jù)類型自動(dòng)推斷字段類型,字段類型可以省略
  var name = "huanghongbo"
  // _ 下劃線表示一個(gè)占位符,編譯器會(huì)根據(jù)變量的數(shù)據(jù)類型賦予相應(yīng)的初始值
  //使用 _ 占位符進(jìn)行賦初始值的時(shí)候,數(shù)據(jù)類型必須指定
  var nickName: String = _
  var age: Int = _
  var numDouble: Double = _
  var flag: Boolean = _
  // 使用val修飾的變量不能使用占位符
  //  val test:Int = _
  val num = 30
  var a = 20
  //如果對(duì)String類型賦值為null,必須指定類型,否則默認(rèn)類型為Null
  var address: String = null
  // 類中的私有字段,有私有的getter 和 setter方法
  // 可以在類的內(nèi)部訪問,也可以被其伴生對(duì)象訪問
  private var hobby = "旅游"
  //對(duì)象的私有字段,訪問權(quán)限更小,只能在當(dāng)前類中訪問
  private[this] var cardInfo = "10010"
  def hello(message: String): Unit = {
    //只能在當(dāng)前類中訪問cardInfo
    println(s"$message   , $cardInfo    ,   $hobby")
  }
  //自定義方法實(shí)現(xiàn)兩個(gè)數(shù)據(jù)求和
  def addNum(num1: Int, num2: Int): Int = {
    num1 + num2
  }
}

object ClassDemo {
  def main(args: Array[String]): Unit = {
    //使用無參構(gòu)造器創(chuàng)建對(duì)象,小括號(hào)可以省略
    val person = new Person()
    var p = new Person
    println(s"${person.name}     ${person.nickName}     ${person.age}     ${person.numDouble}      ${person.flag}")
    //給類的屬性賦值
    person.age = 50
    //注意:這種方式賦值其實(shí)就是調(diào)用 age_= 這個(gè)setter方法
    person.age_=(20)
    //這種就是調(diào)用了他的getter方法
    println(person.age)
    //調(diào)用類中的方法
    person.hello("你好")
    println(person.addNum(1, 3))
    //無法調(diào)用類中的私有字段和對(duì)象的私有字段
    //    person.hobby
    //    person.cardInfo

  }
}

自定義Getter和Setter方法

對(duì)于 Scala 類中的每一個(gè)屬性,編譯后會(huì)有一個(gè)私有的字段和相應(yīng)的getter、setter方法生成。如果屬性是私有的,生成的getter、setter方法也是私有的

package hhb.cn.part04

/**
 * @description:
 * @date: 2020-09-24 10:48
 **/
class Dog {
  private var _leg = 0

  private var test1 = 0
  var test2 = 0

  //自定義getter方法
  def leg: Int = _leg

  //自定義setter方法
  def leg_=(newLeg: Int) {
    _leg = newLeg
  }

}
object GetterAndSetterDemo {
  def main(args: Array[String]): Unit = {
    val dog = new Dog
    //調(diào)用自定義的setter方法
    dog.leg_=(10)
    //調(diào)用自定義的getter方法
    println(dog.leg)

    //私有的屬性不自定義setter方法,直接使用報(bào)錯(cuò)
//    dog.test1_=(1)
    dog.test2_=(1)
  }
}
反編譯.png

自定義變量的getter和setter方法需要遵循以下原則:

  • 字段屬性名以"_"作為前綴,如:_leg
  • getter 方法定義為:def leg = _leg
  • setter 方法定義為:def leg_=(newLeg:Int)

Bean屬性

JavaBean規(guī)范把Java屬性定義為一堆getter和setter方法。類似于Java,當(dāng)將Scala字段標(biāo)注為 @BeanProperty時(shí),getFoo和setFoo方法會(huì)自動(dòng)生成。使用@BeanProperty并不會(huì)影響Scala自己自動(dòng)生成的getter和setter方法。在使用時(shí)需要導(dǎo)入包scala.beans.BeanProperty

package hhb.cn.part04

import scala.beans.BeanProperty

/**
 * @description:
 * @date: 2020-09-24 11:05
 **/
class Teacher {
  @BeanProperty var name: String = _
}

object BeanDemo {
  def main(args: Array[String]): Unit = {

    val teacher = new Teacher
    teacher.name = "張三"
    teacher.name_=("李四")
    println(teacher.name)

    //@BeanProperty 生成的set、get方法
    teacher.setName("王五")
    println(teacher.getName)
  }
}

構(gòu)造器

如果沒有定義構(gòu)造器,Scala類中會(huì)有一個(gè)默認(rèn)的無參構(gòu)造器;Scala當(dāng)中類的構(gòu)造器分為兩種:主構(gòu)造器和輔助構(gòu)造器;主構(gòu)造器的定義與類的定義交織在一起,將主構(gòu)造器的參數(shù)直接放在類名之后。當(dāng)主構(gòu)造器的參數(shù)不用var或val修飾時(shí),參數(shù)會(huì)生成類的私有val成員。Scala中,所有的輔助構(gòu)造器都必須調(diào)用另外一個(gè)構(gòu)造器,另外一個(gè)構(gòu)造器可以是輔助構(gòu)造器,也可以是主構(gòu)造器。

package hhb.cn.part04

//主構(gòu)造器與類名交織在一起,類名后面的參數(shù)就是主構(gòu)造器的參數(shù)
//主構(gòu)造器直接在類中,其代碼不包含在任何方法中
class Animal(name: String, age: Int) {
  //下面三行代碼也屬于主構(gòu)造器的方法
  println(name)
  println(age)
  println("=================================")

  var gender: String = ""

  def this(name: String, age: Int, gender: String) {
    //在類中,每個(gè)輔助構(gòu)造器必須以主構(gòu)造器或其他輔助構(gòu)造器的調(diào)用作為第一行代碼
    this(name, age)
    this.gender = gender
  }

  var color: String = ""

  def this(name: String, age: Int, gender: String, color: String) {
    //此處調(diào)用的是其他構(gòu)造器
    this(name, age, gender)
    this.color = color
  }
}

object ConstructorDemo {

  def main(args: Array[String]): Unit = {
    val animal1 = new Animal("啦啦1", 5)
    val animal2 = new Animal("啦啦2", 6,"公")
    val animal3 = new Animal("啦啦3", 7,"公","紅色")
  }
}

備注:每個(gè)輔助構(gòu)造器必須以主構(gòu)造器或其他輔助構(gòu)造器的調(diào)用作為第一行代碼

對(duì)象

單例對(duì)象

Scala并沒有提供Java那樣的靜態(tài)方法或靜態(tài)字段;

可以采用object關(guān)鍵字實(shí)現(xiàn)單例對(duì)象,具備和Java靜態(tài)方法同樣的功能;

使用object語法結(jié)構(gòu)【object是Scala中的一個(gè)關(guān)鍵字】達(dá)到靜態(tài)方法和靜態(tài)字段的目的;對(duì)象本質(zhì)上可以擁有類的所有特性,除了不能提供構(gòu)造器參數(shù);

對(duì)于任何在Java中用單例對(duì)象的地方,在Scala中都可以用object實(shí)現(xiàn):

  • 作為存放工具函數(shù)或常量的地方
  • 高效地共享單個(gè)不可變實(shí)例
package hhb.cn.part04

/**
 * @description:
 * @author: huanghongbo
 **/
object Object {
  println("這是一個(gè)單例對(duì)象?。。?)

  def printInfo: Unit = {
    println("Hello Scala Object")
  }
}

object ObjectDemo {

  def main(args: Array[String]): Unit = {
    // 只會(huì)輸出一次這是單例對(duì)象
    //    val object1 = Object
    //    val object2 = Object
    // 同樣只會(huì)輸出一次這是單例對(duì)象
    Object.printInfo
    Object.printInfo

  }
}

Scala中的單例對(duì)象具有如下特點(diǎn):

  1. 創(chuàng)建單例對(duì)象不需要使用new關(guān)鍵字
  2. object中只有無參構(gòu)造器
  3. 主構(gòu)造代碼塊只能執(zhí)行一次,因?yàn)樗菃卫?/li>

伴生類與伴生對(duì)象

當(dāng)單例對(duì)象與某個(gè)類具有相同的名稱時(shí),它被稱為這個(gè)類的“伴生對(duì)象”;

類和它的伴生對(duì)象必須存在于同一個(gè)文件中,而且可以相互訪問私有成員(字段和方法);

package hhb.cn.part04

//伴生類和伴生對(duì)象在一個(gè)文件中,文件名相同
/**
 * 伴生類
 */
class ClassObject {

  private var name = "hhb"

  def printInfo: Unit = {
    //在伴生類中可以訪問伴生對(duì)象的私有成員
    println(ClassObject.num)
    println("Hello Object!")
  }

}

/**
 * 伴生對(duì)象
 */
object ClassObject {
  private var num = 10

  def main(args: Array[String]): Unit = {
    val classObject = new ClassObject
    //在伴生對(duì)象中可以訪問伴生類的私有成員
    println(classObject.name)
    classObject.printInfo
  }
}

應(yīng)用程序與對(duì)象

每個(gè)Scala應(yīng)用程序都必須從一個(gè)對(duì)象的main方法開始,這個(gè)方法的類型為 Array[String] => Unit;

備注:main方法寫在class中是沒有意義的,在IDEA中這樣的 class 連run的圖標(biāo)都不能顯示

除了main方法以外,也可以擴(kuò)展App特質(zhì)(trait)

package hhb.cn.part04

/**
 * App源碼里面有一個(gè)main方法。我們不需要寫了,只需要直接繼承App即可
 */
object AppDemo extends App {

  println("Hello Spark")

}

apply方法

object 中有一個(gè)非常重要的特殊方法 -- apply方法;

  • apply方法通常定義在伴生對(duì)象中,目的是通過伴生類的構(gòu)造函數(shù)功能,來實(shí)現(xiàn)伴生對(duì)象的構(gòu)造函數(shù)功能;

  • 通常我們會(huì)在類的伴生對(duì)象中定義apply方法,當(dāng)遇到類名(參數(shù)1,...參數(shù)n)時(shí)apply方法會(huì)被調(diào)用;

  • 在創(chuàng)建伴生對(duì)象或伴生類的對(duì)象時(shí),通常不會(huì)使用new class/class() 的方式,而是直接使用 class()隱式的調(diào)用伴生對(duì)象的 apply 方法,這樣會(huì)讓對(duì)象創(chuàng)建的更加簡潔;

package hhb.cn.part04

class Student(name: String, age: Int) {

  private var gender: String = _

  def sayHi: Unit = {
    println(s"大家好,是$name , 年齡 $age, $gender 生")
  }

}

object Student extends App {


  def apply(name: String, age: Int): Student = new Student(name, age)

  //直接使用class類名(參數(shù)……) 這種方式隱式調(diào)用伴生對(duì)象中apply方法創(chuàng)建class Student 對(duì)象
  val student = Student("zhangs", 20)

  //伴生類和伴生對(duì)象可以相會(huì)訪問私有成員
  student.gender = "男"

  student.sayHi
}

問題:在Scala中實(shí)現(xiàn)工廠方法,讓子類聲明哪種對(duì)象應(yīng)該被創(chuàng)建,保持對(duì)象創(chuàng)建在同一位置。例如,假設(shè)要?jiǎng)?chuàng)建Animal工廠,讓其返回Cat和Dog類的實(shí)例,基于這個(gè)需求,通過實(shí)現(xiàn)Animal伴生對(duì)象的apply方法,工廠的使用者可以像這樣創(chuàng)建新的Cat和Dog實(shí)例。

package hhb.cn.part04

abstract class Animal {
  def speak
}

class Dog extends Animal {
  override def speak: Unit = {
    println("============")
  }
}

class Cat extends Animal {
  override def speak: Unit = {
    println("+++++++++++")
  }
}

object Animal {

  def apply(message: String): Animal = {
    if (message == "dog") {
      new Dog
    } else {
      new Cat
    }
  }

  def main(args: Array[String]): Unit = {
    val cat = Animal("cat")
    cat.speak

    val dog = Animal("dot")
    dog.speak
  }
}
第四個(gè)模塊錯(cuò)題集.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 類 類是對(duì)象的模板,在scala中可以使用new關(guān)鍵字聲明同結(jié)構(gòu)的對(duì)象class Person { privat...
    hipeer閱讀 322評(píng)論 0 0
  • 如果類沒有內(nèi)容(body),可以省略空的花括號(hào),只寫一行語句 類可以帶參數(shù),類名稱C之后圓括號(hào)內(nèi)的參數(shù),x稱為類參...
    wangdy12閱讀 922評(píng)論 0 0
  • 類 定義類 類的定義用關(guān)鍵字class 定義Student學(xué)生類,Student是類名稱,建議列名稱首字母大寫。類...
    _羊羽_閱讀 428評(píng)論 0 0
  • 類定義 Kotlin 類可以包含:構(gòu)造函數(shù)和初始化代碼塊、函數(shù)、屬性、內(nèi)部類、對(duì)象聲明。Kotlin 中使用關(guān)鍵字...
    bruce1990閱讀 436評(píng)論 0 0
  • PS:有小伙伴說配圖有點(diǎn)意思,嘿嘿,今天的配圖是: 把這四節(jié)的內(nèi)容都掌握了,基本就算kotlin入門了不逼逼,開始...
    coder_pig閱讀 4,146評(píng)論 8 9

友情鏈接更多精彩內(nèi)容