天天看點

Scala 必知必會

文章目錄

  • ​​入門​​
  • 概述
  • 安裝
  • Java VS Scala
  • val 和 var
  • 基本資料類型
  • lazy在Scala中的應用
  • 開發工具IDEA
  • Maven
  • ​​函數​​
  • 方法定義
  • 預設參數
  • 命名參數
  • 可變參數
  • 條件語句
  • 循環語句
  • ​​面向對象​​
  • 類的定義和使用
  • 抽象類
  • 伴生類和伴生對象
  • case和trait
  • ​​集合​​
  • 數組
  • List
  • Set
  • Map
  • Optuon&Some&None
  • Tuple
  • ​​模式比對​​
  • 基本類型
  • 類型比對
  • 異常處理
  • ​​進階函數​​
  • 字元串
  • 匿名函數
  • Currying
  • 高階函數

入門

https://www.scala-lang.org/

Scala combines object-oriented and functional programming in one concise, high-level language. Scala’s static types help avoid bugs in complex applications, and its JVM and JavaScript runtimes let you build high-performance systems with easy access to huge ecosystems of libraries.
  • 學習Scala的意義:

    Spark、Kafka、Flink

    優雅

    開發速度快

    融合到生态圈

  1. 安裝 Java 8
  2. 下載下傳 download scala 網址:https://www.scala-lang.org/download/2.11.8.html
  3. 解壓 unzip scala
  4. 配置環境變量(可選)

    Windows 需配置兩個 Path中: D:\scala\bin 和 D:\scala\jre\bin

    Scala 必知必會
  5. 檢視是否生效
Scala 必知必會
Linux或Mac中操作步驟:
1.tar -zxvf scala-2.11.8.tgz -C  解壓路徑
2.到解壓目錄下 pwd  複制整個路徑 
3.将上面的路徑 添加到環境變量中
  vi ~/.bash_profile
    export SCALA_HOME=複制的路徑
    export PATH=$SCALA_HOME/bin:$Path
  儲存
  source ~/.bash_profile
  echo $SCALA_HOME

下載下傳之後的scala目錄下的bin目錄中 有普通檔案 和 .bat檔案
.bat檔案是在Windows中用的,Linux或Mac中用不到,是以可以删掉 rm *.bat

      

Java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World..");
  }
}
      
Scala(每行代碼并不強求使用;結束,但是Java是必須的)
Object HelloWorld {
  def main(args : Array[String]) {
    println("Hello World..")
  }
}
      

Scala 必知必會
  • val:值
final
val 值名稱:類型=xx
val a = 1  (不可變)
val a : int = 1
      
  • var:變量
var 值名稱:類型=xxx
var b = 1
var b : int = 1
      

  • Byte/Char
  • Short/Int/Long/Float/Double
  • Boolean

    隻有Float聲明時比較特别

  • var c : Float = 1.1f
scala> b=20
b: Int = 20

scala> val b:Int =10
b: Int = 10

scala> val c:Boolean=true
c: Boolean = true

scala> val d =1.1
d: Double = 1.1

scala> val e:Float=1.2f
e: Float = 1.2

      
Scala 必知必會
Scala 必知必會
Scala 必知必會

​lazy var d : int = 1;​

延遲加載,隻有在第一次使用時才加載

讀取檔案并以字元串形式輸出

import scala.io.Source._
var info = fromFile("...").mkString
      

如果用lazy var info = fromFile("…").mkString,開始是檢測不到錯誤的,要小心使用

*注意:當一個變量聲明為lazy,隻有當你第一次操作時才會去真正通路,如果不去通路,即使寫錯了,也不會發現

Scala 必知必會

1.下載下傳IDEA和Maven
2.進入IDEA,建立項目 選擇Maven 勾選create from archetype 選擇scala-archetype simple-> 正常建立(注意Maven倉庫位置)
3.IDEA預設是不支援Scala的,需要下載下傳Scala插件
  File -> settings -> Plugins -> install JetBrains plugin -> scala
  之後就可以new 一個Scala類了
4.建立測試類,運作報錯
  删除pom.xml中<arg>make:transitive</args>

      
Scala 必知必會
Scala 必知必會
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>untitled5</artifactId>
<version>1.0-SNAPSHOT</version>
<inceptionYear>2008</inceptionYear>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<encoding>UTF-8</encoding>
<scala.version>2.11.8</scala.version>
<spark.version>2.3.0</spark.version>
</properties>

<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>

<!--引入Spark Core的依賴-->
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-core -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.3.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-sql -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.12</artifactId>
<version>2.4.3</version>
</dependency>

</dependencies>

<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>2.15.0</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</args>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.6</version>
<configuration>
<useFile>false</useFile>
<disableXmlReport>true</disableXmlReport>
<!-- If you have classpath issue like NoDefClassError,... -->
<!-- useManifestOnlyJar>false</useManifestOnlyJar -->
<includes>
<include>**/*Test.*</include>
<include>**/*Suite.*</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</project>


      
Scala 必知必會
Scala 必知必會

函數

Scala 必知必會
def 方法名(參數: 參數類型): 傳回值類型 = {
    //方法體

    //最後一行作為傳回值(不需要使用return)
  }

  def max(x: Int, y: Int): Int = {
    if(x > y)
      x
    else 
      y
  }


      
package org.example

object App {

  def main(args: Array[String]): Unit = {
    println(add(2,5))
  }


  def add(x:Int,y:Int):Int={
  x+y
  }
}

      
7
package org.example

object App {

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

    println(three())
    //沒有入參的時候可以不用寫
    println(three)
  }




  def three()=1+2
}
      
Scala 必知必會

無傳回值 自動加​

​Unit​

預設參數: 在函數定義時,允許指定參數的預設值
//參數
  def sayName(name: String ) = {
    println(name)
  }

  //預設參數
  def sayName1(name: String ="Jack") = {
    println(name)
  }


//main調用

    sayName("jaja")
    sayName1()
    sayName1("Ma")
      

jaja

Jack

Ma

可以修改參數的傳入順序
def speed(destination: Float, time: Float): Float {
      destination / time
    }

    println(speed(100, 10))
    println(speed(time = 10, destination = 100))

      

變參數(可傳入任意多個相同類型的參數) java中 int… numbers

JDK5+:可變參數

def sum(number: Int*) =  {
    var result = 0
    for(num <- number) {
      result += num
    }
    result
  }

      
相關源碼:org.apache.spark.sql.Dataset中的select方法
      

Scala 必知必會

  • to 1 to 10 (左閉右閉) 1.to(10)
  • range Range(1,10) (左閉右開的) Range(1,10,2) (2為步長)
  • until 1 until 10 (左閉右開)

to、until的底層調用都是Range

scala> 1 to 10
res1: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> Range(1,10)
res2: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> 1.to(10)
res3: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> Range(1,10,2)
res4: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)

scala> Range(1,10,5)
res5: scala.collection.immutable.Range = Range(1, 6)

scala> Range(10,1,-1)
res8: scala.collection.immutable.Range = Range(10, 9, 8, 7, 6, 5, 4, 3, 2)

scala> 1 until 10
res9: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)

      
  • for
for(i <- 1.to(10)) {
    println(i)
  }
  for(i <- 1.until(10, 2)) {
    println(i)
  }
  for(i <- 1 to 10 if i % 2 == 0) {
    println(i)
  }

  val courses = Array("Hadoop", "Spark SQL", "Spark Streaming", "Storm", "Scala")
  for(x<- courses) {
    println(x)
  }

  //x其實就是courses裡面的每個元素
  // =>    就是将左邊的x作用上一個函數,變成另外一個結果


  courses.foreach(x=> println(x))

      
  • while
var (num, sum) = (100, 0)
  while(num > 0){
    sum = sum + num
    num = num - 1
  }
  println(sum)
      

面向對象

  • Java/Scala OO(Object Oriented)
  • 封裝:屬性、方法封裝到類中,可設定通路級别
  • 繼承:父類和子類之間的關系 重寫
  • 多态:父類引用指向子類對象 開發架構基石
Person person = new Person();
User user = new User();
Person person =new User();
      

package org.example

object ObjectApp {
  def main(args: Array[String]): Unit = {
    val person = new People()
    person.name = "Messi"
    //    println(person.name + ".." + person.age)
    println("invoke eat method: " + person.eat)
    person.watchFootball("Barcelona")

    person.printInfo()
    //編譯不通過 private 修飾
    //    println(person.gender)
  }

}



class People{
  //var(變量)類型自動生成getter/setter
  //這種寫法就是一個占位符
  var name: String = _
  //val(常量)類型自動生成getter
  val age: Int = 10

  private [this] var gender = "male"

  def printInfo() : Unit = {
    print("gender: " + gender)
  }

  def eat(): String = {
    name + " eat..."
  }

  def watchFootball(teamName: String): Unit = {
    println(name + " is watching match of " + teamName)
  }
}



      

invoke eat method: Messi eat…

Messi is watching match of Barcelona

gender: male

###繼承和重寫

  • 繼承

    class Student(name: String, age: Int, var major: String) extends Person(name, age) {}

  • 重寫
override def acquireUnrollMemory()
  override def toString = "test override"
      
package org.example

object ConstructorApp {
  def main(args: Array[String]): Unit = {
    var person =new Person("zhangsan",99)
    println(person.age+":"+person.name)
    var person2 =new Person("zhangsan",99,"Man")
    println(person2.age+":"+person2.name+";"+person2.gender)
  }

}
//主構造器
class Person(val name: String, val age: Int){
  println("Person constructor enter...")

  val school = "ustc"
   //占位符肯定要預先指定類型
  var gender: String = _

  //附屬構造器
  def this(name: String , age: Int, gender: String){
    //必須要調用主構造器或者其他附屬構造器
    this(name, age)
    this.gender = gender
  }

  override def toString = "test override"
  println("Person Constructor leave...")

}

//繼承
//name: String, age: Int, var major: String 繼承父類的可以不用直接寫var 否則需要重新申明
class Student(name: String, age: Int, var major: String) extends Person(name, age) {

  //重寫
  override val school = "pku"
  println("Person Student enter...")

  println("Person Student leave...")
}


      

Scala 必知必會
package org.example

object AbstractApp {
  def main(args: Array[String]): Unit = {
    var stu =new Student1();
    println(stu.age)
    println(stu.name)
    stu.speak;
  }
}
abstract class Person3{
  def speak

  val name: String
  val age: Int
}

class Student1 extends Person3{
  override def speak: Unit = {
    println("speak")
  }
  override val name: String = "Messi"
  override val age: Int = 32
}

      

如果有一個​

​class​

​,還有一個與​

​class​

​同名的​

​object​

​ 互為 伴生類和伴生對象
class ApplyTest{
    def apply(){
      println(...)
    }
  } 
  object ApplyTest{
    def apply(){
      println("Object ApplyTest apply...")
      new ApplyTest
    }
  } 
      

類名() ==> Object.apply

對象() ==> Class.apply

最佳實踐:在Object的apply方法中去new一個Class

package org.example


object ApplyApp {
  def main(args: Array[String]): Unit = {
//    for(i<-1 to 10){
//      ApplyTest.incr
//    }
//    //object 是一個單例對象
//    println(ApplyTest.count)

    var b=ApplyTest()
    //預設走的是object=》apply
    //Object ApplyTest apply...

    println("-----------------------")
    var c= new ApplyTest()
    c()
    //Class ApplyTest apply...
  }

}
class ApplyTest {
  def apply() = {
    println("Class ApplyTest apply...")

  }

}
object ApplyTest {
  println("Object start...")
  var count = 0
  def incr={
    count=count+1
  }
  def apply() = {
    println("Object ApplyTest apply...")

    //在object中的apply中new class
    new ApplyTest
  }
  println("Object end...")
}

      

case class :不用new

​case class Dog(name: String)​

直接可以調用​

​Dog("wangcai")​

Trait: 類似implements
xxx entends ATrait 
xxx extends Cloneable with Logging with Serializable
      
源碼中Partition類
Scala 必知必會

集合

package org.example

object ArrayApp extends App{
  //println("hello")
  val a = new Array[String](5)
  a(0)="hello"
  println(a(0))

  val b = Array("hello","world")

  val c = Array(1,2,3,4,5,67)
  c.sum
  c.max
  c.mkString("/")

}



      
Scala 必知必會
val d=scala.collection.mutable.ArrayBuffer[Int]()

  d+=1
  d+=2
  d+=(2,33,4)
  d++=Array(33,45,22)
  println(d+"-------------------")
  d.insert(0,999)
  d.remove(1,2)
  d.trimEnd(2)
  println(d+"-------------------")
  //轉化成不可變的
  d.toString()
  for(i<-0 until d.length){

    println(c(i))
  }

      

hello

ArrayBuffer(1, 2, 2, 33, 4, 33, 45, 22)-------------------

ArrayBuffer(999, 2, 33, 4, 33)-------------------

1

2

3

4

5

Scala 必知必會

list是不可變的,對list進行添加删除或者取值等操作均會傳回一個新的list。
scala> Nil
res4: scala.collection.immutable.Nil.type = List()
      
scala> Nil
res4: scala.collection.immutable.Nil.type = List()

scala> val l= List(1,2,3,4,5,56)
l: List[Int] = List(1, 2, 3, 4, 5, 56)

scala> l.head
head   headOption

scala> l.head
res5: Int = 1

scala> l.tail
tail   tails

scala> l.tail
res6: List[Int] = List(2, 3, 4, 5, 56)

scala> l.tails
res7: Iterator[List[Int]] = non-empty iterator

scala>


      
Scala 必知必會
val d=scala.collection.mutable.ArrayBuffer[Int]()

  d+=1
  d+=2
  d+=(2,33,4)
  d++=Array(33,45,22)
  d++ =List(1,2,3,4,)

      

set是一個非重複的集合,若有重複資料,則會自動去重。
scala> val set = Set(1,2,3,1,2,5)
set: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 5)



      

map是K-V鍵值對集合。
package org.example

object MapApp {

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

    val map = Map(
      "1" -> "hello" ,
      2 -> "world",
      3 -> "!!!!!"
    )
    println(map.mkString(","))

    println("-----------------------")
    for(x<-map){
      println(x._1+":"+x._2)
    }

    println("-----------------------")

    var keys = map.keys
    var keyIterator = keys.iterator
    while(keyIterator.hasNext) {
      val key = keyIterator.next()
      println(key + "\t" + map.get(key).get)
    }



  }

}


      
1 -> hello,2 -> world,3 -> !!!!!
-----------------------
1:hello
2:world
3:!!!!!
-----------------------
1 hello
2 world
3 !!!!!


      

val map = Map(
      "1" -> "hello" ,
      2 -> "world",
      3 -> "!!!!!"
    )

    println(map.get(2))
    println(map.get(999))

      

Some(world)

None

option.scala

@SerialVersionUID(5066590221178148012L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
case object None extends Option[Nothing] {
  def isEmpty = true
  def get = throw new NoSuchElementException("None.get")
}


@SerialVersionUID(1234815782226070388L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
final case class Some[+A](x: A) extends Option[A] {
  def isEmpty = false
  def get = x
}

      

與清單一樣,與清單不同的是元組可以包含不同類型的元素。元組的值是通過将單個的值包含在圓括号中構成的。建立過程可加new關鍵詞,也可不加。
package org.example

object TupleApp {

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

    var t=new Tuple3[Int,Int,String](1,99,"hello")
    println(t.toString())

    println("----------------")

    var t2=(9999,"hello")

    println(t2.toString())

    println(t2.swap.toString())


  }

}




      
(1,99,hello)
----------------
(9999,hello)
(hello,9999)


      

模式比對

Java : 對一個值進行條件判斷例如​

​switch​

模式比對類似于java的switch case。Scala的模式比對不僅可以比對值還可以比對類型、從上到下順序比對,如果比對到則不再往下比對、都比對不上時,會比對到case _ ,相當于default、match 的最外面的”{ }”可以去掉看成一個語句。

def match_test(m:Any) = {
       m match {
         case 1 => println("nihao")
         case m:Int => println("Int")
         case _ => println("default")
       }
    }

      
package org.example

object MarchApp {

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

    def judeGrade(grade:String)={
      grade match{
        case "B" => println("Just so so")
        case "A" => println("good")
        case "S" => println("cool")
        case _=> println("No.1")
      }

    }
    judeGrade("S")
    judeGrade("A")
    judeGrade("SSS")
  }

}



```scala
> cool
good
No.1



### Array


```scala


    def greeting(array:Array[String]) = {
      array match {
        case Array("zs")=> println("hi,zs")
        case Array(x,y)=> println(x+"and"+y)
        case Array("zs",_*)=>println("zs and other")
        case _=>println("everyone")
      }
    }

    greeting(Array("zs"))
    greeting(Array("zs","ls"))


      

hi,zs

zsandls

def greeting1(list:List[String]) = {
      list match {
        case "zs"::Nil=> println("hi,zs")
        case x::y::Nil=> println(x+"and"+y)
        case "zs"::tail =>println("zs and other")
        case _=>println("everyone")
      }
    }

    greeting1(List("zs"))
    greeting1(List("zs","ls"))
  }

      

def matchType(obj: Any) = {
      obj match {
        case x: Int => println("hi,int")
        case y: String => println(y)
        case m: Map[_, _] => println("map")
        case _ => println("everyone")
      }
    }

    matchType(Map(1 -> "yes"))
    matchType(11)
    matchType("hello")
  }

      

try{
      val i=10/0
      println(i)

    }catch {
      case e:ArithmeticException=>println(e.getMessage)
      case e:Exception=>println(e.getMessage)
    }finally {

    }


      
/ by zero

進階函數

  • 插值
val s ="hello"
  val name="jacksun"
  println(s+name)
  println(s+":"+name)

  println(s"hello:$name")

      
  • 多行字元串
//多行
  var d =
    """
      |1
      |2
      |3
      |4
      |5
      |5
      |6
    """.stripMargin
      

匿名函數分為有參匿名函數、無參匿名函數、有傳回值的匿名函數。(可以将匿名參數的傳回給一個val聲明的值,匿名函數不能顯式的聲明傳回值)
package org.example

object FunctionApp extends App {

  //有參數匿名函數
  val printy = (a : Int) => {
    println(a)
  }
  printy(999)

  //無參數匿名函數
  val printx = ()=>{
    println("Scala No.1")
  }
  printx()

  //有傳回值的匿名函數
  val add = (a:Int,b:Int) =>{
    a+b
  }
  println(add(4,4))

}


      

将接受一個參數的轉化成2個
def add(a:Int,b:Int) = a+b
println(add(2,1))
//Currying
def add2(a:Int)(b:Int) = a+b
println(add2(2)(1))

      

高階函數(Higher-Order Function)就是操作其他函數的函數。

Scala 中允許使用高階函數, 高階函數可以使用其他函數作為參數,或者使用函數作為輸出結果。

object Test {
   def main(args: Array[String]) {

      println( apply( layout, 10) )

   }
   // 函數 f 和 值 v 作為參數,而函數 f 又調用了參數 v
   def apply(f: Int => String, v: Int) = f(v)

   def layout[A](x: A) = "[" + x.toString() + "]"

}


      
  • map

對每個集合的元組進行操作

scala> val l =List(1,2,3,4,5,6,7,8,9)
l: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> l.map(x=>(x+1))
res5: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> l.map((x:Int)=>x*2)
res6: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18)

scala> l.map(x=>x*2)
res7: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18)

scala> l.map(_*2)


      
  • filter

    過濾條件

scala> l.filter(_>5)
res9: List[Int] = List(6, 7, 8, 9)

      
  • take

    取數

scala> l.take(1)
res10: List[Int] = List(1)

scala> l.take(3)
res11: List[Int] = List(1, 2, 3)

      
  • reduce

    兩兩相加相減

scala> l.take(3).reduce(_-_)
res15: Int = -4
// 從左相減
scala> l.take(3).reduceLeft(_-_)
res16: Int = -4
// 從右相減
scala> l.take(3).reduceRight(_-_)
res17: Int = 2