1. 程式人生 > >Scala入門到精通——第十四節 Case Class與模式匹配(一)

Scala入門到精通——第十四節 Case Class與模式匹配(一)

本節主要內容

  1. 模式匹配入門
  2. Case Class簡介
  3. Case Class進階

1. 模式匹配入門

在java語言中存在switch語句,例如:

//下面的程式碼演示了java中switch語句的使用
public class SwitchDemo {
    public static void main(String[] args) {
        for(int i = 0; i < 100; i++) { 
            switch (i) {
            case 10:System.out.println("10");
                break
; //在實際編碼時,程式設計師很容易忽略break語句 //這容易導致意外掉入另外一個分支 case 50:System.out.println("50"); case 80:System.out.println("80"); default: break; } } } }

scala解決了java語言中存在的這個問題,scala解決這一問題的利器就是模式匹配,上面的java程式碼可以利用scala語言的模式匹配來避免,程式碼如下:


object PatternMatching extends App{
  for(i<- 1 to 100){
    i  match {
      case 10 => println(10)
      case 50 => println(50)
      case 80 => println(80)
      case _ => 
    }
  }
}

上述scala程式碼展示瞭如何使用scala中的模式匹配,它的實現方式是通過match關鍵字與 case X=>的方式實現的,其中case _表示除了 case 10,case 50,case 80的其餘匹配,類似於java中的default。但scala語言中提供了更為靈活的匹配方式,如:

object PatternMatching extends App{
  for(i<- 1 to 100){
    i  match {
      case 10 => println(10)
      case 50 => println(50)
      case 80 => println(80)
      //增加守衛條件
      case _ if(i%4==0)=> println(i+":能被4整除")
      case _ if(i%3==0)=> println(i+":能被3整除")
      case _ =>
    }
  }
}

case語言中還可以加相應的表示式,例如:

object PatternMatching extends App{
  var list=new ArrayBuffer[Int]()
  var x=0
  for(i<- 1 to 100){
    i  match {
      //後面可以跟表示式
      case 10 => x=10
      case 50 => println(50)
      case 80 => println(80)
      case _ if(i%4==0)=> list.append(i)
      case _ if(i%3==0)=> println(i+":能被3整除")
      case _ =>
    }
  }
  println(x)
}

2 Case Class簡介

Case Class一般被翻譯成樣例類,它是一種特殊的類,能夠被優化以用於模式匹配,下面的程式碼定義了一個樣例類:

//抽象類Person
abstract class Person

//case class Student
case class Student(name:String,age:Int,studentNo:Int) extends Person
//case class Teacher
case class Teacher(name:String,age:Int,teacherNo:Int) extends Person
//case class Nobody
case class Nobody(name:String) extends Person

object CaseClassDemo{
  def main(args: Array[String]): Unit = {
    //case class 會自動生成apply方法,從而省去new操作
    val p:Person=Student("john",18,1024)  
    //match case 匹配語法  
    p  match {
      case Student(name,age,studentNo)=>println(name+":"+age+":"+studentNo)
      case Teacher(name,age,teacherNo)=>println(name+":"+age+":"+teacherNo)
      case Nobody(name)=>println(name)
    }
  }
}

當一個類被聲名為case class的時候,scala會幫助我們做下面幾件事情:
1 構造器中的引數如果不被宣告為var的話,它預設的話是val型別的,但一般不推薦將構造器中的引數宣告為var
2 自動建立伴生物件,同時在裡面給我們實現子apply方法,使得我們在使用的時候可以不直接顯示地new物件
3 伴生物件中同樣會幫我們實現unapply方法,從而可以將case class應用於模式匹配,關於unapply方法我們在後面的“提取器”那一節會重點講解
4 實現自己的toString、hashCode、copy、equals方法
除此之此,case class與其它普通的scala類沒有區別

下面給出case class Student位元組碼檔案內容,以驗證我們上述所講的內容:

//下面的程式碼是自動生成的伴生物件中的位元組碼內容
D:\ScalaWorkspace\ScalaChapter13\bin\cn\scala\xtwy>javap -private Student$.class

Compiled from "CaseClass.scala"
public final class cn.scala.xtwy.Student$ extends scala.runtime.AbstractFunction
3<java.lang.String, java.lang.Object, java.lang.Object, cn.scala.xtwy.Student> i
mplements scala.Serializable {
  public static final cn.scala.xtwy.Student$ MODULE$;
  public static {};
  public final java.lang.String toString();
  public cn.scala.xtwy.Student apply(java.lang.String, int, int);
  public scala.Option<scala.Tuple3<java.lang.String, java.lang.Object, java.lang
.Object>> unapply(cn.scala.xtwy.Student);
  private java.lang.Object readResolve();
  public java.lang.Object apply(java.lang.Object, java.lang.Object, java.lang.Ob
ject);
  private cn.scala.xtwy.Student$();
}

//下面的程式碼是Student類自身的位元組碼內容
D:\ScalaWorkspace\ScalaChapter13\bin\cn\scala\xtwy>javap -private Student.class
Compiled from "CaseClass.scala"
public class cn.scala.xtwy.Student extends cn.scala.xtwy.Person implements scala
.Product,scala.Serializable {
  private final java.lang.String name;
  private final int age;
  private final int studentNo;
  public static scala.Function1<scala.Tuple3<java.lang.String, java.lang.Object,
 java.lang.Object>, cn.scala.xtwy.Student> tupled();
  public static scala.Function1<java.lang.String, scala.Function1<java.lang.Obje
ct, scala.Function1<java.lang.Object, cn.scala.xtwy.Student>>> curried();
  public java.lang.String name();
  public int age();
  public int studentNo();
  public cn.scala.xtwy.Student copy(java.lang.String, int, int);
  public java.lang.String copy$default$1();
  public int copy$default$2();
  public int copy$default$3();
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public scala.collection.Iterator<java.lang.Object> productIterator();
  public boolean canEqual(java.lang.Object);
  public int hashCode();
  public java.lang.String toString();
  public boolean equals(java.lang.Object);
  public cn.scala.xtwy.Student(java.lang.String, int, int);
}

3. case class應用實戰

1 case class常用方法
前面我們提到,定義case class便會自動生成對應的toString,hashCode,equals,copy等方法,

//toString方法演示
scala> val s=Teacher("john",38,1024)
s: Teacher = Teacher(john,38,1024)

//無參copy方法演示
scala> val s1=s.copy()
s1: Teacher = Teacher(john,38,1024)

//copy方法是深度拷貝
scala> println(s eq s1)
false

//equal方法根據物件內容進行比較
scala> println(s equals s1)
true

scala> println(s == s1)
true

//hashcode方法
scala> s1.hashCode
res45: Int = 567742485

//toString方法
scala> s1.toString
res46: String = Teacher(john,38,1024)

//帶一個引數的copy方法
scala> s1.copy(name="stephen")
res47: Teacher = Teacher(stephen,38,1024)
//帶二個引數的copy方法
scala> s1.copy(name="stephen",age=58)
res49: Teacher = Teacher(stephen,58,1024)
//帶三個引數的copy方法
scala> s1.copy(name="stephen",age=58,teacherNo=2015)
res50: Teacher = Teacher(stephen,58,2015)

2 多個引數的case class

abstract class Person

case class Student( name:String, age:Int, studentNo:Int) extends Person

case class Teacher( name:String, age:Int, teacherNo:Int) extends Person

case class Nobody( name:String) extends Person

//SchoolClass為接受多個Person型別引數的類
case class SchoolClass(classDescription:String,persons:Person*)

//下列程式碼給出的是其模式匹配應用示例
object CaseClassDemo{
  def main(args: Array[String]): Unit = {
     val sc=SchoolClass("學途無憂網Scala培訓班",Teacher("搖擺少年夢",27,2015),Student("搖擺少年夢",27,2015))
     sc match{
       case SchoolClass(_,_,Student(name,age,studetNo))=>println(name)
       case _ => println("Nobody")
     }
  }
}

3 sealed case class

在進行模式匹配的時候,有些時候需要確保所有的可能情況都被列出,此時常常會將case class的超類定義為sealed(密封的) case class,如:

//Person最前面加了個關鍵字sealed
sealed abstract class Person

case class Student( name:String, age:Int, studentNo:Int) extends Person

case class Teacher( name:String, age:Int, teacherNo:Int) extends Person

case class Nobody( name:String) extends Person

case class SchoolClass(classDescription:String,persons:Person*)

object CaseClassDemo{
  def main(args: Array[String]): Unit = {
     val s:Person=Student("john",18,1024)
     //這邊僅僅給出了匹配Student的情況,在編譯時
     //編譯器會提示
     //match may not be exhaustive. It would fail on the following inputs: Nobody(_), Teacher(_, _, _)
     s match{
       case Student(name,age,studentNo)=>println("Student")
     }
  }
}

編譯器給出的提示可以通過下列語句進行消除,

//下面的語句達到了sealed class的要求
 val s:Person=Student("john",18,1024)
     s match{
       case Student(name,age,studentNo)=>println("Student")
       case Teacher(name,age,studentNo)=>println("Teacher")
       case Nobody(name)=>println("Nobody")
     }

4 case class在實用應用中的其它用途
某個類一旦被定義為case class,則編譯器會自動生成該類的伴生物件,伴生物件中包括了apply方法及unapply方法,apply方法使得我們可以不需要new關鍵字就可以建立物件,而unapply方法,則使得可以方便地應用在模式匹配當中,另外編譯器還自動地幫我們實現對應的toString、equals、copy等方法。在實際中,case class除了在模式匹配時能發揮其強大的威力之外,在進行其它應用時,也顯示出了其強大的功能,下面給出case class在SparkSQL中的應用,旨在說明case class在實際應用中的重要地位。

新增公眾微訊號,可以瞭解更多最新Spark、Scala相關技術資訊
這裡寫圖片描述