伴生對象 vs 伴生類
Scala中的伴生對象
對於Object內部的方法,我們可以直接通過Object.method(對象名.方法)來調用
類似於Java中的static
舉例:現在object裏面有個increment方法
object Timer {
var count = 0
def increment():Long = {
count += 1
count
}
}
在scala的shell裏運行:
scala> object Timer {
| var count = 0
| def increment():Long = {
| count += 1
| count
| }
| }
defined object Timer
scala> Timer.increment
res33: Long = 1
scala> Timer.increment
res34: Long = 2
scala> Timer.increment
res35: Long = 3
伴生對象 和伴生類 是相輔相成的
class A
object A
class A 叫做object A 的伴生類
object A 叫做class A 的伴生對象
object ApplyDemo {
def main(args: Array[String]): Unit = {
for(i <- 1 to 10){
ApplyTest.increment
}
println(ApplyTest.count)
}
}
class ApplyTest{
}
object ApplyTest{
println("object ApplyTest enter...")
var count = 0
def increment = {
count = count + 1
count
}
println("object ApplyTest leave...")
}
輸出結果:
object ApplyTest enter...
object ApplyTest leave...
10
Process finished with exit code 0
從上面可以看出,你調用object裏的方法,會觸發它把object裏所有的東西先執行一遍,方法不調用不執行(increment 只是定義了,沒有調用),在for裏面觸發了,每次把它加1,所以看到的是先打印兩句話,然後把count打印出來。
有點想不明白,for循環裏循環10次,不應該觸發10次object的方法嗎,爲什麼不把那兩句話打印10次?
上面代碼變一下:
object ApplyDemo {
def main(args: Array[String]): Unit = {
ApplyTest.static
}
}
object ApplyTest{
println("object ApplyTest enter...")
def static: Unit = {
println("object ApplyTest static")
}
println("object ApplyTest leave...")
}
輸出結果:
object ApplyTest enter...
object ApplyTest leave...
object ApplyTest static
Process finished with exit code 0
調用object.static方法,觸發object裏所有東西執行一遍,方法不調用不執行,先打印object裏的兩句話,然後,運行那個static方法,有了第三行。
再升級:
package com.ruozedata.bigdata.scala04
object ApplyDemo {
def main(args: Array[String]): Unit = {
val a = new ApplyTest() //new 一個 ApplyTest這個類 類要用的時候先new出來
println(a) //這個println其實調用的是toString方法,包名+類名@hashcode
}
}
class ApplyTest{
}
object ApplyTest{
println("object ApplyTest enter...")
def static: Unit = {
println("object ApplyTest static")
}
println("object ApplyTest leave...")
}
輸出結果:
//因爲調用的是toString方法,就是包名+類名@hashcode 所以是下面的:
com.ruozedata.bigdata.scala04.ApplyTest@1b604f19
Process finished with exit code 0
再變一下:
package com.ruozedata.bigdata.scala04
object ApplyDemo {
def main(args: Array[String]): Unit = {
val a = new ApplyTest()
println(a)
a.method() //調用a裏的method方法
}
}
class ApplyTest{
def method(): Unit ={
println("class ApplyTest method")
}
}
object ApplyTest{
println("object ApplyTest enter...")
def static: Unit = {
println("object ApplyTest static")
}
輸出結果:
com.ruozedata.bigdata.scala04.ApplyTest@1b604f19
class ApplyTest method
Process finished with exit code 0
再擴展一下,下面這個很重要:
package com.ruozedata.bigdata.scala04
object ApplyDemo {
def main(args: Array[String]): Unit = {
//平時我們都是new的
//現在沒有new,直接=類名() 其實調用的是object對象裏面的apply方法
//apply方法被觸發,觸發之後再new出來
val b = ApplyTest() //沒有new
b.method()
}
}
class ApplyTest{
def method(): Unit ={
println("class ApplyTest method")
}
def apply() ={
println("class apply method invoked...")
}
}
object ApplyTest{
println("object ApplyTest enter...")
var count = 0
def apply() ={ //apply它底層返回的是下面new出來的ApplyTest這個類
println("object apply method invoked...")
new ApplyTest() //在這裏new 一個ApplyTest類出來
}
println("object ApplyTest leave...")
}
輸出結果:
object ApplyTest enter...
object ApplyTest leave...
object apply method invoked...
class ApplyTest method
Process finished with exit code 0
記住:
遇到 xxx = 類名() 一定是調用object對象裏的apply方法,然後new出來
還有:
object ApplyDemo {
def main(args: Array[String]): Unit = {
val c = new ApplyTest()
println( c )
println(c()) //c是一個引用,c()調用的是Class的apply方法,不常用
}
}
class ApplyTest{
def method(): Unit ={
println("class ApplyTest method")
}
def apply() ={
println("class apply method invoked...")
}
}
object ApplyTest{
println("object ApplyTest enter...")
def apply() ={ //apply它底層返回的是下面new出來的ApplyTest這個類
println("object apply method invoked...")
new ApplyTest() //在這裏new 一個ApplyTest類出來
}
println("object ApplyTest leave...")
}
輸出結果:
com.ruozedata.bigdata.scala04.ApplyTest@1b604f19
class apply method invoked...
Process finished with exit code 0
記住兩句話:
①遇到 xxx = 類名() 一定是調用object對象裏的apply方法,然後new出來。(在apply裏做了什麼?new 一個,然後最後一行返回)
②如果是對象/引用(就是上面new出來的),它調用的是Class.apply方法
舉例:
在創建一個數組的時候,
var array = Array(“a”,“b”,“c”)
點擊Array,可以看到它調用的就是object對象裏的apply方法
把一個函數賦給一個變量
object FunctionAdApp {
def main(args: Array[String]): Unit = {
val sayHelloFunc = sayHello _ //就是這樣寫的,空格加一個下劃線
println(sayHelloFunc("zhangsan"))
}
def sayHello(name:String)={
println("hello : " + name)
}
}
匿名函數
匿名函數(參數名:參數類型)=>函數體
//定義了一個匿名函數,並把匿名函數賦值給了一個變量(也可以賦值給一個函數)
object FunctionNoNameApp {
def main(args: Array[String]): Unit = {
val add = (x:Int,y:Int) =>{ x+y }
println(add(2,3))
}
}
函數的克里化currying
spark的udf函數裏面經常會用到
object FunctionCurryingApp {
def main(args: Array[String]): Unit = {
println(sum(12,2))
println(add(12)(2)) //調用
}
def sum(x:Int,y:Int)= x+y //正常是這樣的
def add(x:Int)(y:Int)= x+y // 函數的克里化currying
}
隱式轉換
在面試scala的時候必問的題目。
隱式:偷偷摸摸的進行
目的:悄悄的爲一個類的方法進行增強
目的:把一個Man悄悄的變爲一個SuperMan
悄悄的爲Man這個類增加一個方法,fly,讓Man這個類會fly
舉例:
object ImplicitApp {
def main(args: Array[String]): Unit = {
//定義隱式轉換
//入參是一個man,進去一個man,返回值是一個Superman,出來一個Superman
implicit def man2superman(man:Man):SuperMan = new SuperMan(man.name)
val man = new Man("xijinping")
man.fly() //man裏面本來是沒有的,隱式轉換後就有fly方法了
}
}
class Man(val name:String){
def eat(): Unit ={
println(s"$name can eat....")
}
}
class SuperMan(val name:String){
def fly(): Unit ={
println(s"$name can fly....")
}
}
輸出結果:
xijinping can fly....
Process finished with exit code 0
再舉個讀文件增強的例子:
package com.ruozedata.bigdata.scala04
import java.io.File
import scala.io.Source
object ImplicitApp2 {
def main(args: Array[String]): Unit = {
implicit def file2RichFile(file:File):RichFile = new RichFile(file)
val file = new File("E://output.txt")
val content = file.read()
println(content)
}
}
class RichFile(val file:File){
def read() = Source.fromFile(file.getPath).mkString
}
模式匹配
變量 match {
case 值1 => 代碼
case 值2 => 代碼
case _ =>代碼
}
舉例:
import scala.util.Random
object MatchApp {
def main(args: Array[String]): Unit = {
val regions =Array("anhui","shanghai","jiangsu","zhejiang")
val region = regions(Random.nextInt(regions.length)) //獲取一個隨機數
println(region)
region match {
case "anhui" => println("anhui")
case "shanghai" => println("shanghai")
case "jiangsu" => println("jiangsu")
case _ => println("zhejiang")
}
}
上面工作中不怎麼用,用的最多的還是在scala的異常處理裏面。
比如在打開文件,去讀取文件,涉及文件IO讀取的時候,最好加上try catch。
try{
代碼
}
catch{
case //異常處理匹配 這裏用
case
case
…
}
finally{
比如關閉文件等操作
}
模式既可以匹配值,也可以匹配類型
類型比如說,異常的類型有哪些,看看能匹配到哪個類型。
偏函數 PartitialFunction
工作或面試用的比較多。
模式匹配也可以用偏函數來實現。
先看一下正常的模式匹配怎麼寫:
import scala.util.Random
object MatchApp {
def main(args: Array[String]): Unit = {
val regions =Array("anhui","shanghai","jiangsu","zhejiang")
val region = regions(Random.nextInt(regions.length))
println(region)
person(region)
def person(city:String)= city match {
case "anhui" => println("anhui person")
case "shanghai" => println("shanghai person")
case "jiangsu" => println("jiangsu person")
case _ => println("zhejiang person")
}
}
}
然後看一下偏函數怎麼寫:
import scala.util.Random
object MatchApp {
def main(args: Array[String]): Unit = {
val regions =Array("anhui","shanghai","jiangsu","zhejiang")
val region = regions(Random.nextInt(regions.length))
println(region)
println(person2(region))
def person2:PartialFunction[String,String] = { //一個偏函數,第一個String可以理解是傳進來的參數,第二個String可以理解是返回值
case "anhui" => "anhui person"
case "shanghai" => "shanghai person"
case "jiangsu" => "jiangsu person"
case _ => "zhejiang person"
}
}
}
偏函數沒有match 。
Tuple
val a = (1,2,3,4,5,6)
括號裏面放了一堆東西,你就可以理解爲Tuple
然後直接可以這樣直接用:
a._1
a._3
舉例:
val a = ("aaa",111,"bbb")
val b= a._2 //就代表第二個
val c= a._3 //就代表第三個
println(b)
println(c)
輸出結果:
111
bbb
Process finished with exit code 0