溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Scala之Object的具體使用(小結)

發布時間:2020-10-08 12:29:32 來源:腳本之家 閱讀:212 作者:leesf 欄目:編程語言

一、前言

前面學習了Scala的Methods,接著學習Scala中的Object

二、Object

Object在Scala有兩種含義,在Java中,其代表一個類的實例,而在Scala中,其還是一個關鍵字,本篇首先將會把object當成一個類的實例看待,展示如何將對象從一種類型轉化為另一種類型,之后將展示如何創建單例對象,Scala中還存在包對象,在Scala中,經常有如下定義

type Throwable = java.lang.Throwable
type Exception = java.lang.Exception
type Error = java.lang.Error
type Seq[+A] = scala.collection.Seq[A]
val Seq = scala.collection.Seq

使用type定義可以使得代碼更為簡潔,可使用伴生對象來創建靜態方法,并且伴生對象可以使得在創建類對象時不需要使用new關鍵字,如下所示

val siblings = List(Person("Kim"), Person("Julia"), Person("Kenny"))

2.1 對象轉化

1. 問題描述

你需要將一個類的實例從一種類型轉化為另一種類型,如動態創建對象

2. 解決方案

使用asInstanceOf方法進行類型轉化,如下的lookup方法返回的對象將給轉化為Recognizer對象 

val recognizer = cm.lookup("recognizer").asInstanceOf[Recognizer]

以上代碼在Java中如下 

Recognizer recognizer = (Recognizer)cm.lookup("recognizer");

asInstanceOf方法是定義在Any類中的,所以任何類中都可以使用該方法

3. 討論

在動態編程中,經常需要從一個類轉化為另一個類,如在Spring框架中使用ApplicationContext文件來初始化Bean 

// open/read the application context file
val ctx = new ClassPathXmlApplicationContext("applicationContext.xml")
// instantiate our dog and cat objects from the application context
val dog = ctx.getBean("dog").asInstanceOf[Animal]
val cat = ctx.getBean("cat").asInstanceOf[Animal]

在進行數字類型轉化時,也可以使用asInstanceOf方法

Scala之Object的具體使用(小結)

當需要與Java進行交互時,也可以使用asInstanceOf方法  

val objects = Array("a", 1)
val arrayOfObject = objects.asInstanceOf[Array[Object]]
AJavaClass.sendObjects(arrayOfObject)

與Java類似,類型轉化可能會拋出ClassCastException異常

Scala之Object的具體使用(小結)

可以使用try/catch來解決此問題

2.2 與Java的.class對應的方法

1. 問題描述

當一個API需要你傳遞Class對象,在Java中,你可以使用.class,但是在Scala則行不通

2. 解決方案

使用Scala的classOf方法,如下所示  

val info = new DataLine.Info(classOf[TargetDataLine], null)

在Java中則使用如下  

info = new DataLine.Info(TargetDataLine.class, null);

classOf方法定義在Predef對象中,因此可以在沒有import情況直接使用

3. 討論

該方法可以讓你開始學習反射,如下示例可以訪問String類的方法

Scala之Object的具體使用(小結)

2.3 確定對象的類

1. 問題描述

在Scala中,你不需要顯示的聲明類型,你偶爾也想要打印一個對象的類或類型以明白Scala的工作機制

2. 解決方案

你可以使用對象的getClass方法來確定Scala為你自動賦值的類型,如當需要了解可變參數的工作流程時,可以調用getClass方法,得知不同情況,類型也不相同 

def printAll(numbers: Int*) {
  println("class: " + numbers.getClass)
}

當使用多個參數調用printAll方法和不使用參數調用printAll方法,其結果不相同

Scala之Object的具體使用(小結)

當處理Scala的XML庫時,該方法非常有效,可以知道在不同情形下所處理的類,如下,<p>標簽下包含了一個子類

Scala之Object的具體使用(小結)

當在<p>標簽中添加<br/>標簽后,其結果如下

Scala之Object的具體使用(小結)

3. 討論

若在IDE中無法得知對象的類型,可以使用getClass方法來獲取對象類型

Scala之Object的具體使用(小結)

2.4 使用對象啟動應用

1. 問題描述

你想要使用main方法來啟動一個應用,或者為腳本提供一個入口

2. 解決方案

啟動應用有兩種方法,其一讓類繼承App,其二是定義一個對象并定義main方法

對于第一種方法,其通用做法如下 

object Hello extends App {
  println("Hello, world")
}

此時object內的語句會自動執行,第二種方法是定義main方法

object Hello2 {
  def main(args: Array[String]) {
    println("Hello, world")
  }
}  

3. 討論

上述兩種方法中,都是通過object來啟動應用的

2.5 使用Object創建單例

1. 問題描述

你想要創建一個單例對象

2. 解決方案

使用object關鍵字來創建單例對象  

object Hello2 {
  def main(args: Array[String]) {
    println("Hello, world")
  }
}  

由于CashRegister被定義為成object,因此僅僅只有一個實例,被調用的方法就相當于Java中的靜態方法,調用如下 

object Main extends App {
  CashRegister.open
  CashRegister.close
}

在創建工具方法時,該方法同樣有效  

import java.util.Calendar
import java.text.SimpleDateFormat

object DateUtils {
  // as "Thursday, November 29"
  def getCurrentDate: String = getCurrentDateTime("EEEE, MMMM d")
  
  // as "6:20 p.m."
  def getCurrentTime: String = getCurrentDateTime("K:m aa")
  
  // a common function used by other date/time functions
  private def getCurrentDateTime(dateTimeFormat: String): String = {
    val dateFormat = new SimpleDateFormat(dateTimeFormat)
    val cal = Calendar.getInstance()
    dateFormat.format(cal.getTime())
  }
}

由于方法是定義在object中,因此可以直接使用DateUtils來調用這些方法,如同Java中調用靜態方法 

DateUtils.getCurrentDate
DateUtils.getCurrentTime

在使用actors時,單例對象可以產生可重用的消息,如果你有可以接受和發送消息的actor,你可以使用如下方法創建單例 

case object StartMessage
case object StopMessage

這些對象將會被作為消息,并且可被傳遞到actor中  

inputValve ! StopMessage
outputValve ! StopMessage

3. 討論

當使用伴生對象時,一個類就既可以有非靜態方法又可以有靜態方法

2.6 使用伴生對象創建靜態成員

1. 問題描述

你想要為一個類創建實例方法和類方法,但是Scala中沒有static關鍵字

2. 解決方案

在class中定義非靜態成員,在object中定義靜態成員,對象與類要有相同的名字并且位于同一個文件中,該對象稱為伴生對象

使用該方法可以讓你創建靜態成員(字段和方法) 

// Pizza class
class Pizza (var crustType: String) {
  override def toString = "Crust type is " + crustType
}

// companion object
object Pizza {
  val CRUST_TYPE_THIN = "thin"
  val CRUST_TYPE_THICK = "thick"
  def getFoo = "Foo"
}

Pizza類和Pizza對象在同一個文件中(Pizza.scala),Pizza對象中的成員等效于Java類中的靜態成員 

println(Pizza.CRUST_TYPE_THIN)
println(Pizza.getFoo)

你也可按照常規方法創建Pizza對象

var p = new Pizza(Pizza.CRUST_TYPE_THICK)
println(p)

3. 討論

class和object具有相同的名稱并且在同一個文件中,class中定義的是非靜態成員,object中定義的是靜態成員

class和其伴生對象可以互相訪問對方的私有成員,如下面object的double方法可以訪問class中的私有變量secret  

class Foo {
  private val secret = 2
}

object Foo {
  // access the private class field 'secret'
  def double(foo: Foo) = foo.secret * 2
}

object Driver extends App {
  val f = new Foo
  println(Foo.double(f)) // prints 4
}

如下的class類中的非靜態方法可以訪問伴生對象中的靜態私有變量 

class Foo {
  // access the private object field 'obj'
  def printObj { println(s"I can see ${Foo.obj}") }
}

object Foo {
  private val obj = "Foo's object"
}

object Driver extends App {
  val f = new Foo
  f.printObj
}

2.7 將常用代碼放在包對象中

1. 問題描述

你想要使方法、字段和其他代碼處于包級別,而不需要class或者object

2. 解決方案

將代碼放置在包對象下面,如將你的代碼放置在package.scala文件中,例如,如果你想要代碼被com.hust.grid.leesf.model包下所有類可用,那么創建一個位于com/hust/grid/leesf/model目錄下的package.scala文件,在package.scala中,在包聲明中移除model,并且以其作為名字來創建包,大致如下 

package com.hust.grid.leesf

package object model {

其他代碼放置在model中,如下所示

package com.hust.grid.leesf

package object model {
  // field
  val MAGIC_NUM = 42

  // method
  def echo(a: Any) { println(a) }
  
  // enumeration
  object Margin extends Enumeration {
    type Margin = Value
    val TOP, BOTTOM, LEFT, RIGHT = Value
  }
  
  // type definition
  type MutableMap[K, V] = scala.collection.mutable.Map[K, V]
  
  val MutableMap = scala.collection.mutable.Map
}

此時,在com.hust.grid.leesf.model包下面類、對象、接口等可以隨意訪問上述定義的字段、方法等

package com.hust.grid.leesf.model

object MainDriver extends App {
  // access our method, constant, and enumeration
  echo("Hello, world")
  echo(MAGIC_NUM)
  echo(Margin.LEFT)
  // use our MutableMap type (scala.collection.mutable.Map)
  val mm = MutableMap("name" -> "Al")
  mm += ("password" -> "123")
  for ((k,v) <- mm) printf("key: %s, value: %s\n", k, v)
}

3. 討論

最疑惑的是將package對象放在哪里,其包名和對象名

如果你想要讓你的代碼在com.hust.grid.leesf.model包中可見,那么將package.scala放在com/hust/grid/leesf/model目錄下,而在package.scala中,其包名應該如下 

package com.hust.grid.leesf

然后使用model作為對象名

package object model {

最后大致如下  

package com.hust.grid.leesf

package object model {

包中可以存放枚舉類型、常量和隱式轉換

2.8 不使用new關鍵字來創建對象實例

1. 問題描述

當不使用new關鍵字來創建對象時,Scala代碼會顯得相對簡潔,如下所示  

val a = Array(Person("John"), Person("Paul"))

2. 解決方案

有兩種方式

  • 為類創建伴生對象,然后定義apply方法,其簽名與構造方法簽名相同
  • 將類定義為case類

為Person對象定義了伴生對象,然后定義apply方法并接受參數

class Person {
  var name: String = _
}

object Person {
  def apply(name: String): Person = {
    var p = new Person
    p.name = name
    p
  }
}

現在你可以不使用new關鍵字來創建Person對象了  

val dawn = Person("Dawn")
val a = Array(Person("Dan"), Person("Elijah"))

Scala編譯器會對伴生對象中的apply進行特殊處理,讓你不使用new關鍵字即可創建對象

將類定義為case類,并且接受相應參數  

case class Person (var name: String)

現在可以采用如下方法創建對象

val p = Person("Fred Flinstone")

Scala會為case類的伴生對象創建apply方法

3. 討論

編譯器會對伴生對象的apply做特殊處理,這是Scala的語法糖  

val p = Person("Fred Flinstone")

上述代碼會被轉化為如下代碼 

val p = Person.apply("Fred Flinstone")

apply方法是工廠方法,Scala的此語法糖讓你不用new關鍵字即可創建對象

可以在伴生對象中創建多個apply方法,這樣相當于多個構造函數

class Person {
  var name = ""
  var age = 0
}

object Person {
  // a one-arg constructor
  def apply(name: String): Person = {
    var p = new Person
    p.name = name
    p
  }

  // a two-arg constructor
  def apply(name: String, age: Int): Person = {
    var p = new Person
    p.name = name
    p.age = age
    p
  }
}

可以使用如下方法創建對象  

val fred = Person("Fred")
val john = Person("John", 42)

為了給case類創建多個構造函數,需要知道case類背后的邏輯

當使用scala編譯器編譯case類時,你會發生其生成了兩個文件,Person$.class和Person.class文件,當使用javap反編譯Person$.class文件時,其輸出如下

Scala之Object的具體使用(小結)

其包含了一個返回Person對象的apply方法 

public Person apply(java.lang.String);

String對應的是case類中的name  

case class Person (var name: String)

使用javap命令可以看到在Person.class中為name生成的getter和setter函數

Scala之Object的具體使用(小結)

在如下代碼中,存在case類和apply方法 

// want accessor and mutator methods for the name and age fields
case class Person (var name: String, var age: Int)

// define two auxiliary constructors
object Person {
  def apply() = new Person("<no name>", 0)
  def apply(name: String) = new Person(name, 0)
}

由于name和age都是var的,所以會生成getter和setter,在object中定義了兩個apply函數,因此可以使用如下三種方式來生成Person對象 

object Test extends App {
  val a = Person()
  val b = Person("Al")
  val c = Person("William Shatner", 82)
  println(a)
  println(b)
  println(c)
  // test the mutator methods
  a.name = "Leonard Nimoy"
  a.age = 82
  println(a)
}

其結果如下  

Person(<no name>,0)
Person(Al,0)
Person(William Shatner,82)
Person(Leonard Nimoy,82)

2.9 使用apply來實現工廠方法

1. 問題描述

為了讓子類聲明應該創建哪種類型的對象,并且只在一處能夠創建對象,你想要實現工廠方法

2. 解決方案

可以使用伴生對象的apply方法來實現工廠方法,你可將工廠實現算法放置在apply方法中

假設你想要創建一個Animal工廠,并且返回Cat和Dog,在Animal類的伴生對象中實現apply方法,你就可以使用如下方式創建不同對象 

val cat = Animal("cat") // creates a Cat
val dog = Animal("dog") // creates a Dog

首先需要創建一個Animal的trait  

trait Animal {
  def speak
}

然后在相同文件中創建伴生對象,創建實現Animal的類,一個合適的apply方法 

object Animal {
  private class Dog extends Animal {
    override def speak { println("woof") }
  }
  
  private class Cat extends Animal {
    override def speak { println("meow") }
  }
  
  // the factory method
  
  def apply(s: String): Animal = {
    if (s == "dog") new Dog
    else new Cat
  }
}

然后就可以使用如下語句創建不同對象

val cat = Animal("cat") // creates a Cat
val dog = Animal("dog") // creates a Dog

3. 討論

如果不使用apply方法來實現工廠方法,也可以使用如下的getAnimal方法來實現上述功能  

// an alternative factory method (use one or the other)
def getAnimal(s: String): Animal = {
  if (s == "dog") return new Dog
  else return new Cat
}

然后可以使用如下方法創建不同對象 

val cat = Animal.getAnimal("cat") // returns a Cat
val dog = Animal.getAnimal("dog") // returns a Dog

以上兩種方法都是可行的

三、總結

本篇學習了Scala中的object及其相應的用法,其在Scala的實際編程中應用也是非常廣泛。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女