其實(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)用框架:
- Lift
- Play