java程序員學(xué)習(xí)scala總結(jié)

其實(shí)幾個(gè)月前有學(xué)過(guò)scala,但是最近看kafka源碼的時(shí)候發(fā)現(xiàn)還有挺多語(yǔ)法看不懂的,于是又重新學(xué)習(xí)了下scala的語(yǔ)法。
本篇博客主要記錄scala和java不一樣的那些地方,建議有java基礎(chǔ)的人看。很多基礎(chǔ)語(yǔ)法,比如if..else,while循環(huán)等都不會(huì)寫(xiě)下來(lái),所以不建議零基礎(chǔ)的人看。需要全面學(xué)習(xí)scala的同學(xué)可以去http://www.runoob.com/scala/scala-tutorial.html這里學(xué)。

一、基礎(chǔ)語(yǔ)法

scala中,行的最后可以不用';' ,加不加都可以

定義包比較靈活

一種是和java一樣在文件頭部定義包

package com.runoob
class HelloWorld

一種是類似C#的方式

package com.runoob {
  class HelloWorld 
}

使用這種方式可以在一個(gè)文件內(nèi)定義多個(gè)包

引用

和java一樣用import導(dǎo)入包。

import java.awt.Color  // 引入Color
import java.awt._  // 引入包內(nèi)所有成員
def handler(evt: event.ActionEvent) { // java.awt.event.ActionEvent
  ...  // 因?yàn)橐肓薺ava.awt,所以可以省去前面的部分
}

import語(yǔ)句可以出現(xiàn)在任何地方,而不是只能在文件頂部。import的效果從開(kāi)始延伸到語(yǔ)句塊的結(jié)束。這可以大幅減少名稱沖突的可能性。
如果想要引入包中的幾個(gè)成員,可以使用selector(選取器):

import java.awt.{Color, Font}
// 重命名成員
import java.util.{HashMap => JavaHashMap}
// 隱藏成員
import java.util.{HashMap => _, _} // 引入了util包的所有成員,但是HashMap被隱藏了
import java.util.{List => _, Map => _, Set => _, _} //隱藏多個(gè)成員

二、數(shù)據(jù)類型&變量

多出來(lái)的類型

scala相比java,多出來(lái)的一些數(shù)據(jù)類型

數(shù)據(jù)類型 描述
Unit 表示無(wú)值,和其他語(yǔ)言中void等同。用作不返回任何結(jié)果的方法的結(jié)果類型。Unit只有一個(gè)實(shí)例值,寫(xiě)成()。
Null null 或空引用
Nothing Nothing類型在Scala的類層級(jí)的最低端;它是任何其他類型的子類型。
Any Any是所有其他類的超類
AnyRef AnyRef類是Scala里所有引用類(reference class)的基類

在Scala中,是沒(méi)有基本類型的,也就是說(shuō),可以對(duì)數(shù)字等基礎(chǔ)類型調(diào)用方法。

多行字符串的表示方法

多行字符串用三個(gè)雙引號(hào)來(lái)表示分隔符,格式為:""" ... """

val foo = """菜鳥(niǎo)教程
www.runoob.com
www.w3cschool.cc
www.runnoob.com
以上三個(gè)地址都能訪問(wèn)"""

變量和常量的聲明

變量聲明用var,常量聲明用val。常量的作用和java的final類似

可以同時(shí)聲明幾個(gè)變量

val xmax, ymax = 100,200  // xmax聲明為100, ymax聲明為200
val xmax, ymax = 100  // xmax, ymax都聲明為100

訪問(wèn)修飾符

scala中訪問(wèn)修飾符只有public、protected、private。默認(rèn)都為public。

在 scala 中,對(duì)Protected成員的訪問(wèn)比 java 更嚴(yán)格一些。因?yàn)樗辉试S保護(hù)成員在定義了該成員的的類的子類中被訪問(wèn)。而在java中,用protected關(guān)鍵字修飾的成員,除了定義了該成員的類的子類可以訪問(wèn),同一個(gè)包里的其他類也可以進(jìn)行訪問(wèn)。

有意思的是,在scala中,訪問(wèn)修飾符可以通過(guò)使用限定詞強(qiáng)調(diào),格式為

private[x] 
或 
protected[x]

也就是說(shuō),如果你有個(gè)類,只想被某個(gè)包底下的成員使用,那么可以這么做

package kongtrio {
  package test1 {

    private[kongtrio] class ScalaTest {
      val name = "hei"
    }

  }

  package test2 {

    import kongtrio.test1.ScalaTest

    object Test {
      def main(args: Array[String]): Unit = {
        val test = new ScalaTest
        print(test.name)
      }
    }

  }

}

上面的代碼中,ScalaTest和Test在兩個(gè)不同的包中,并ScalaTest還是private的。但是使用了限定詞強(qiáng)調(diào),因此只要是kongtrio包下面的類就都可以看見(jiàn)ScalaTest類,其他包下的類就看不到ScalaTest類了。

三、函數(shù)和方法

Scala 方法是類的一部分,而函數(shù)是一個(gè)對(duì)象可以賦值給一個(gè)變量。換句話來(lái)說(shuō)在類中定義的函數(shù)即是方法。

Scala 中的函數(shù)則是一個(gè)完整的對(duì)象,Scala 中的函數(shù)其實(shí)就是繼承了 Trait 的類的對(duì)象。

Scala 中使用 val 語(yǔ)句可以定義函數(shù),def 語(yǔ)句定義方法。在scala中,可以在方法中定義函數(shù),也可以在函數(shù)中定義函數(shù)。函數(shù)可作為一個(gè)參數(shù)傳入到方法中,而方法不行。

方法聲明

def functionName ([參數(shù)列表]) : [return type]
//無(wú)入?yún)?、無(wú)返回值的函數(shù)。:Unit和'='可以去掉,可寫(xiě)可不寫(xiě)。下面的例子有的有寫(xiě),有的沒(méi)寫(xiě)
def test(){
    
}
//例子.傳入兩個(gè)參數(shù),args表示一個(gè)可變參數(shù),String后面的'*'表示這參數(shù)是可變的,類似java的'...'
//返回一個(gè)String類型的返回值。如果無(wú)返回值,就用Unit表示或者不寫(xiě)
def test(age:Int,args:String*):String = {
    //在scala中,可以不用寫(xiě)return
    "hello world"
}

//可以設(shè)置默認(rèn)值的函數(shù)
def test2(a:Int,b:Int,c:Int=3){}
//如果指定了參數(shù)名,那么a和b的傳入順序可以亂掉
test(b=2,a=1)

//傳名調(diào)用函數(shù)
def time(): Long = {
    println("time 方法內(nèi)")
    System.currentTimeMillis()
}
//time參數(shù)后面加個(gè)'=>'傳名調(diào)用,這時(shí)調(diào)用'test(time())'方法時(shí),會(huì)等真正使用到time()時(shí)才調(diào)用time()方法
//最后輸出
//test 方法內(nèi)
//time 方法內(nèi)
//time = 1533291592954
def test(time: => Long) = {
    println("test 方法內(nèi)")
    println("time = " + time)
}

//匿名函數(shù)
//匿名函數(shù)的語(yǔ)法很簡(jiǎn)單,箭頭左邊是參數(shù)列表,右邊是函數(shù)體。
var inc = (x:Int) => x+1

//偏應(yīng)用函數(shù)
//你不需要提供函數(shù)需要的所有參數(shù),只需要提供部分,或不提供所需參數(shù)
def test(a: Int, b: String) = {
    println("a=" + a + " b  = " + b)
}
//固定第一個(gè)參數(shù)為1,然后構(gòu)造一個(gè)'新的函數(shù)'
val f = test(1, _: String)
f("hello")

//方法內(nèi)定義函數(shù) 
def test() = {
    def f(a: Int) = {
      
    }
}

//高階函數(shù)
//高階函數(shù)可以使用其他函數(shù)作為參數(shù),或者使用函數(shù)作為輸出結(jié)果
def test() = {
    def f(age: Int) = {
      println(age)
      "hello"
    }
    val str = myMethed(f, 2)
    println(str(2))
}
//myMethed第一個(gè)入?yún)⑹且粋€(gè)函數(shù),這個(gè)函數(shù)的入?yún)⑹荌nt,返回值是String,所以用Int=>String表示
//myMethed返回值也是一個(gè)函數(shù),Int => String表示返回的函數(shù)入?yún)⑹荌nt,返回值是String
def myMethed(a: Int => String, b: Int): Int => String = {
    a(b)
    def hei(hei: Int): String = {
      "world" + hei
    }
    hei
}

//函數(shù)柯里化。上面是正常的函數(shù),下面是將函數(shù)柯里化
def add(x:Int,y:Int)=x+y
def add(x:Int)(y:Int) = x + y
//實(shí)現(xiàn)原理大概就是這樣,js里面也可以這么做
def add(x:Int)=(y:Int)=>x+y

方法如果不寫(xiě)等于號(hào)和方法主體,那么方法會(huì)被隱式聲明為抽象(abstract),包含它的類型于是也是一個(gè)抽象類型。

閉包

var factor = 3  
//在一個(gè)函數(shù)內(nèi)部使用其他函數(shù)的變量factor,說(shuō)明這個(gè)函數(shù)是一個(gè)閉包
val multiplier = (i:Int) => i * factor  

四、數(shù)組

//一維數(shù)組
var z = new Array[String](3)
//定義二維數(shù)組
var myMatrix =Array.ofDim[String](3,4)
// 輸出所有數(shù)組元素
for ( x <- myList ) {
    println( x )
}
for (i <- 0 to x.length - 1) {
      println(x(i))
}
//也可以這么輸出
for (i <- x.indices) {
      println(x(i))
}
//創(chuàng)建區(qū)間數(shù)組
//獲取10、12、14、16、18
var myList1 = Array.range(10, 20, 2)
//獲取11、12、13、14、15、16、17、18、19
var myList2 = Array.range(10,20)

五、集合

Scala 集合分為可變的和不可變的集合。不可變的集合仍然可以模擬添加,移除或更新操作。但是這些操作將在每一種情況下都返回一個(gè)新的集合,同時(shí)使原來(lái)的集合不發(fā)生改變。

List

列表是不可變的,值一旦被定義了就不能改變。

val site: List[String] = List("Runoob", "Google", "Baidu")
// 空列表
val empty: List[Nothing] = List()
//構(gòu)造列表的兩個(gè)基本單位是 Nil 和 ::
var list = "hello" :: Nil

連接兩個(gè)列表

def test() {
    var list = "hello" :: Nil
    var list2 = "world" :: Nil
    //用':::'連接兩個(gè)list,輸出List(hello, world)
    //效果和list.concat(list2)一樣。也可以用list++list2
    println(list ::: list2)
    //用'.:::'連接兩個(gè)list,輸出 List(world, hello)
    println(list.:::(list2))
}

通過(guò)一些自帶的函數(shù)創(chuàng)建列表

//List.fill填充
//重復(fù) Runoob 3次
val site = List.fill(3)("Runoob")

//List.tabulate()  通過(guò)給定的函數(shù)來(lái)創(chuàng)建列表 
//創(chuàng)建5個(gè)元素,會(huì)執(zhí)行5次傳入的函數(shù),入?yún)?開(kāi)始到4
val ints = List.tabulate(5)(n => "hello" + n)

添加元素

val nums = List(1)
//在列表頭部添加元素
val ints = 2 +: nums
val ints = nums.+:(2)
val ints = nums.::(2)
//在列表后添加元素
val ints = nums :+ 2
val ints2 = nums.:+(2)

可變的List在scala里面叫ListBuffer

val mylist = ListBuffer(1)
//可以直接對(duì)列表進(jìn)行增刪操作
mylist += 2
mylist += 5
mylist -= 2

Set

Scala 集合分為可變的和不可變的集合。

默認(rèn)情況下,Scala 使用的是不可變集合,如果你想使用可變集合,需要引用 scala.collection.mutable.Set 包。默認(rèn)引用 scala.collection.immutable.Set。

val set = Set(1,2,3)
println(set.exists(_ % 2 == 0)) //true
println(set.drop(1)) //Set(2,3)

可變集合的增刪改查

import scala.collection.mutable.Set
val mutableSet = Set(1,2,3)
mutableSet.add(4)
mutableSet.remove(1)
mutableSet += 5
mutableSet -= 2

連接兩個(gè)集合等操作和List差不多。

下面介紹一些比較好玩的函數(shù)

val set1 = Set(1, 2)
val set2 = Set(2, 3)
//求交集
val set3 = set1 & set2
//求并集
val set4 = set1 | set2
//下面兩個(gè)都是求差集
val set5 = set1 &~ set2
val set6 = set1 -- set2

Map

Map 有兩種類型,可變與不可變,區(qū)別在于可變對(duì)象可以修改它,而不可變對(duì)象不可以。

默認(rèn)情況下 Scala 使用不可變 Map。如果你需要使用可變集合,你需要顯式的引入 import scala.collection.mutable.Map

// Map 鍵值對(duì)演示
val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF")

可變Map

val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF")
colors += "yellow" -> "#888888"
colors.put("green", "hei")

合并兩個(gè)Map

val map1 = Map("red" -> "1")
val map2 = Map("red" -> "2")
//最后結(jié)果是red->2。因?yàn)閙ap2放后面,key相同時(shí),后面的會(huì)覆蓋前面的
val map3 = map1 ++ map2

元組

與列表一樣,元組也是不可變的,但與列表不同的是元組可以包含不同類型的元素。

val t = (4,3,2,1)
//訪問(wèn)元組的值
val sum = t._1 + t._2 + t._3 + t._4
//迭代元組
t.productIterator.foreach{ i =>println("Value = " + i )}

Option

從map中的get返回的值是Option

val myMap: Map[String, String] = Map("key1" -> "value")
val value1: Option[String] = myMap.get("key1")
val value2: Option[String] = myMap.get("key2")
 
println(value1) // Some("value1")
println(value2) // None

六、迭代器

Scala Iterator(迭代器)不是一個(gè)集合,它是一種用于訪問(wèn)集合的方法。

val it = Iterator("Baidu", "Google", "Runoob", "Taobao")  
while (it.hasNext){
    println(it.next())
}

七、Scala類和對(duì)象

scala的一個(gè)文件里面可以定義多個(gè)類。這些類默認(rèn)就是public的,在java中一個(gè)文件內(nèi)最多只能有一個(gè)public的類。

class Point(xc: Int, yc: Int) {
   var x: Int = xc
   var y: Int = yc

   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("x 的坐標(biāo)點(diǎn): " + x);
      println ("y 的坐標(biāo)點(diǎn): " + y);
   }
}

scala的繼承也是單繼承,要重寫(xiě)父類的某個(gè)方法時(shí),必須在方法前面加上override關(guān)鍵字。但是在重寫(xiě)父類的抽象方法時(shí)就不需要加override關(guān)鍵字。

class Location(override val xc: Int, override val yc: Int,
   val zc :Int) extends Point(xc, yc){
   var z: Int = zc
    
   def move(dx: Int, dy: Int, dz: Int) {
      x = x + dx
      y = y + dy
      z = z + dz
      println ("x 的坐標(biāo)點(diǎn) : " + x);
      println ("y 的坐標(biāo)點(diǎn) : " + y);
      println ("z 的坐標(biāo)點(diǎn) : " + z);
   }
   //重寫(xiě)的話需要加override關(guān)鍵字
   override def move(dx: Int, dy: Int){}
}

scala單例對(duì)象

在 Scala 中,是沒(méi)有 static 這個(gè)東西的,但是它也為我們提供了單例模式的實(shí)現(xiàn)方法,那就是使用關(guān)鍵字 object。單例對(duì)象中的所有方法都可以看成是static方法。

單例對(duì)象不能帶參數(shù),單例對(duì)象可以與某個(gè)類共享同一個(gè)名稱,這樣單例對(duì)象就被稱作是這個(gè)類的伴生對(duì)象,而這個(gè)類是這個(gè)單例對(duì)象的伴生類,另外,他們必須要定義在同一個(gè)文件中。

//定義單例對(duì)象,注意,單例對(duì)象不能帶有參數(shù)
object Test {
   def main(args: Array[String]) {
      print("hello world")
   }
}

八、Scala的接口

在scala中,用trait關(guān)鍵字表示接口,scala的接口可以定義屬性和方法的實(shí)現(xiàn)。

trait Equal {
  def isEqual(x: Any): Boolean
  def isNotEqual(x: Any): Boolean = !isEqual(x)
}
class Point(xc: Int, yc: Int) extends Equal {
  var x: Int = xc
  var y: Int = yc
  def isEqual(obj: Any) =
    obj.isInstanceOf[Point] &&
    obj.asInstanceOf[Point].x == x
}

九、Scala的模式匹配

scala的模式匹配其實(shí)就是java的switch,但是比起java更加靈活。

def matchTest(x: Any): Any = x match {
      case 1 => "one"
      case "two" => 2
      //如果傳入的是Int類,就輸出"scala.Int"
      case y: Int => "scala.Int"
      //‘_’ 表示匹配全部
      case _ => "many"
}

十、Scala的異常處理

scala的異常處理和java差不多

object Test {
   def main(args: Array[String]) {
      try {
         val f = new FileReader("input.txt")
      } catch {
         case ex: FileNotFoundException =>{
            println("Missing file exception")
         }
         case ex: IOException => {
            println("IO Exception")
         }
      }
   }
}

十一、scala的兩個(gè)web應(yīng)用框架:

  1. Lift
  2. Play
最后編輯于
?著作權(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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