伴生对象 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