静态关键字不是面向对象中的语法,scala中函数可理解为java中的静态方法java
scala是彻底面向函数的编程语言,scala中的类其实也是一个函数linux
这里的函数相似于java的静态方法,体现的是功能的封装算法
java中方法的声明 public static void(返回值类型) 方法名 (参数列表) throws 异常列表{ 方法体 return "123" } java中方法的调用 new User().test() scala中函数的声明: scala中没有public的概念,也没有静态的概念,因此为了和属性区分开,使用def关键字声明 scala中返回值类型须要放置在方法名的后面,使用冒号分隔 scala中参数列表的声明和java一致,但参数声明有变化: 参数名:参数类型。... scala中没有throws关键字,因此函数根本就不须要抛出异常 scala中函数也能够返回结果 // scala中最基本的函数声明 def test( name : String, age : Int ) : String = { // 方法体 println("name = " + name + ", age = " + age) return "123" } // 调用函数 test("zhangsan", 12)
声明编程
def main(args: Array[String]): Unit = { def hello(name: String): Unit = { println(s"Hello $name") } hello("smile") //1.无参无返回值加Unit def f1(): Unit = { println("无参无返回值") } //f1() //2.有1个参,无返回值 def f2(name: String): Unit = { println("有1个参,无返回值") } //f2("kris") //3.多个参,无返回值 def f3(name: String, age: Int): Unit = { println("多个参,无返回值") } //f3("kris", 22) //4.无参有返回值 声明一个变量接收函数返回值或者直接打印 def f4(): String = { return "abc" } val result: String = f4() println(result) //println(f4()) //5.有参,有返回值 def f5(name: String): String = { return name } println(f5("kris")) }
def fun1(): String = { return "smile" } //println(fun1()) // 方法中若是使用了return关键字,那么方法必定要有返回值类型 // 若是不使用return关键字,那么返回值类型能够省略 // 方法能够自动推断方法返回值类型,根据方法体逻辑中的最后一行代码的结果来推断,因此能够把return省略掉 def fun2() = { "alex" } //println(fun2()) def fun3() : String = { "kris" } //println(fun3()) ////////////////////////////////return////////////////////////////////////// // 函数若是明确了返回值为Unit,那么方法体中若是使用return不起做用 def fun4(): Unit = { return "abc" } //println(fun4()) //() //若是明确方法没有返回值,也不但愿自动推断,那么能够省略 = 等号 def fun5(){ "哈哈" } //println(fun5()) //() // 若是函数没有参数列表,那么参数小括号能够省略,而且调用时,不须要使用小括号 def fun6{ println("省略参数列表") } //fun6 //println(fun6) // 若是方法体的逻辑代码只有一行,那么花括号能够省略,为了区分方法名和方法体,须要使用等号链接 def fun7 = println("Hi") //fun7 //println(fun7) // 之因此使用def关键字来声明函数,就是为了在某些场合和变量区分开 def fun8 = "kris" println(fun8) // 匿名函数 ()->{}
// 在参数类型的后面增长星号,表示可变参数 // 可变参数必须放置在参数列表的后面 def func1(age: Int, name : String* ): Unit ={ println(age + ",\t" + name) } //func1(22, "alex", " smile", "kris") //22, WrappedArray(alex, smile, kris) // 给参数默认值,若是调用方法时,没有传递指定的参数,那么会采用默认值代替 // 若是参数了指定的参数,那么会将默认值覆盖掉 def func2(name : String = "kk", age: Int): Unit ={ println(s"$name, $age") } func2("kris", 22) //kris, 22 def func3(name : String, age : Int): Unit ={ println(s"$name, $age") } // 方法在调用时,传递的参数从左到右依次进行赋值 // 带名参数 func3(age = 21, name = "kk")
(参数类型)=>(返回值类型)windows
// 将函数当成参数 def func1(i: Int): Int ={ 20 + i } def func2(i: Int): Unit ={ 11 + i } def func3(i: Int): Unit ={ println("i:" + i) } // 将符合要求的函数(输入Int,输出Int)做为参数传递给另一个函数 def func01(f: (Int) => Int): Unit ={ println(f(3)) } func01(func1)//23
柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数且返回结果的新函数的技术闭包
def test( a : Int, b:Int, c:Int )--->> def test( a:Int )(b:Int)(c:Int)
实现原理其实就是将每个须要多个条件(参数)的复杂逻辑函数进行拆分,转换为单一条件(参数)的简单函数,经过简单函数的组合解决复杂的逻辑操做,相似于JavaEE中三层架构的感受架构
def test( a:Int )((Int, Int)=>Int)(b:Int)
在柯里化的实现中,使用了闭包的功能,所谓的闭包就是将函数外的局部变量包含到函数内部的一种处理机制,这样能够避免函数外的变量被回收致使程序异常的状况。app
def func3(i: Int): Unit ={ println("i:" + i) } def func02() = { func3 _ //函数当成返回值,把方法返回而不是结果返回,加_ } func02()(4) //i:4 // 函数柯里化(主要目的是完成隐式转换);上面方式有点乱,声明的时候能够直接加两个括号!跟上边的方式是同样的;这种简化就是柯里化,把复杂的业务逻辑拆成一段一段,它不是一个函数; def func03()(i: Int): Unit ={ println(i) } func03()(4) //4
闭包
// 改变局部变量的生命周期,将变量包含到当前函数的内部,造成了一个闭包的效果;执行完f4,它的局部变量i就无效了(弹栈了),f5把它包进去 // scala中函数能够声明在任意的地方 def func04(i: Int)={ def func05(j: Int): Unit ={ println(i + j) } func05 _ } func04(10)(20) //30 def func06(i: Int)(j: Int): Unit ={ //按柯里化方式声明,柯里化底层就是闭包 println(i + j) } //func06(7)(3)
(参数)=>{方法体--把逻辑代码传进来了}
参数只用一次可省略加个_便可(做为占位符)框架
def f1(f:(String) => Unit): Unit ={ f("kris") } def f2(s: String): Unit ={ println("Hello " + s) } //f1(f2) 这种方式还要声明一个函数,太麻烦了,使用匿名函数 // 匿名函数 没有名称也不用声明; 参数=>方法体;能够传一段逻辑代码,扩展性强 遵循了OCP原则,扩展性开放 f1((x) => {println("Hello" + x)}) //Hello kris f1(println(_)) //kris 占位符 //f1((x) => {println(x)}) 进一步的省略,这两种写法是等同的
柯里化练习| 把函数做为参数传进来jvm
Operator(1)(++)(1) -->> 2
def main(args: Array[String]): Unit = { def Operator(i: Int) = { //Operator应该返回一个函数;柯里化也就是闭包的概念,把一个完整的进行拆分一个个单一的函数;Operator并无执行,最终的执行结果应该靠++决定,但须要用它把它返回便可 def Middle(f:(Int, Int)=>Int) = { //第二个括号即Middle,效果是1+1的结果打印出来,应该把一个算法传进来; def Inner(j: Int) ={ //第三个括号 f(i, j) //执行操做,传进两个参数 } Inner _ } Middle _ } def ++(i: Int, j: Int): Int = { //这里执行具体功能,++ i / j //加减乘除均可以;逻辑能够传进来,但主题架构没有变; } println(Operator(1)(++)(1)) //1 }
传函数还要本身声明---->再简化,使用匿名函数
//val i: Int = Operator(1)((x, y) => (x + y))(10) val i: Int = Operator(1)(_+_)(10) //参数只用了一次还能够省略 println(i) //11
//sum(3)(3)(println(_))-->> 6 def sum(i: Int) = { def middle(j: Int)={ def inner(f: (Int, Int) => Unit) = { f(i, j) } inner _ } middle _ } sum(3)(3)((x: Int, y: Int)=>{println(x+y)}) //6 //sum(3)(3)((x, y) => (println(x + y)))
递归有可能栈溢出;(不是内存溢出),栈内存溢出---跟线程有关;压栈; 栈帧(即每块内存)
// 递归 // 1) 函数本身调用本身 // 2) 函数中必定要有跳出的逻辑 // 3) 函数本身调用本身时传递的参数应该有规律 // 4)scala中递归函数须要明确返回值类型 def test(num: Int): Int ={ if (num == 1){ 1 }else{ num*test(num-1) } } println(test(5)) //120
JIT及时编译器,可打乱程序执行顺序
内存加载顺序是由jvm决定,怎么快怎么来
windows、linux是线程优先级,苹果系统是分时
看一个应用场景
惰性计算(尽量延迟表达式求值)是许多函数式编程语言的特性。惰性集合在须要时提供其元素,无需预先计算它们,这带来了一些好处。
首先,您能够将耗时的计算推迟到绝对须要的时候。其次,您能够创造无限个集合,只要它们继续收到请求,就会继续提供元素。
函数的惰性使用让您可以获得更高效的代码。Java 并无为惰性提供原生支持,Scala提供了。
Java实现懒加载的代码,好比经常使用的单例模式懒汉式实现时就使用了上面相似的思路实现
当函数返回值被声明为lazy时,函数的执行将被推迟,直到咱们首次对此取值,该函数才会执行。这种函数咱们称之为惰性函数,在Java的某些框架代码中称之为懒加载(延迟加载)。 def main(args: Array[String]): Unit = { lazy val res = sum(10, 20) println("########################") println("res=" + res) } def sum(n1: Int, n2: Int): Int ={ println("sum() 执行了..") return n1 + n2 } /* 不加lazy sum() 执行了.. ######################## res=30 加lazy ######################## sum() 执行了.. res=30 */
注意事项和细节
1)lazy 不能修饰 var 类型的变量
2)不可是 在调用函数时,加了 lazy ,会致使函数的执行被推迟,咱们在声明一个变量时,若是给声明了 lazy ,那么变量值得分配也会推迟。 好比 lazy val i = 10
函数没返回值就是过程--就是一段逻辑
map函数的做用是将集合中全部的数据进行逻辑操做,数据总量不变。map不支持偏函数,collect支持;
偏函数会致使不符合条件的数据被过滤掉,因此数据总量发生变化;过滤filter、groupBy
① 在对符合某个条件,而不是全部状况进行逻辑操做时,使用偏函数是一个不错的选择
② 将包在大括号内的一组case语句封装为函数,咱们称之为偏函数,它只对会做用于指定类型的参数或指定范围值的参数实施计算,超出范围的值会忽略.
③ 偏函数在Scala中是一个特质PartialFunction
偏函数简化形式
声明偏函数,须要重写特质中的方法,有的时候会略显麻烦,而Scala其实提供了简单的方法
def f2: PartialFunction[Any, Int] = { case i: Int => i + 1 // case语句能够自动转换为偏函数 } val list: List[Any] = List(1, 2, 3, 4, 5, "abc").collect(f2) println(list)
进一步的简化:
val list2 = List(1, 2, 3, 4, 5, "abc").collect { //偏函数,只对其中的一部分起做用 case i: Int => i + 2 //List(3, 4, 5, 6, 7) //再进一步的简化:不用声明函数直接写个case便可 } println(list2) //List(3, 4, 5, 6, 7
扁平化栗子:
val list = List(1,2,List(3,4), "abc") println(list.flatMap(x => List(x))) //List(1, 2, List(3, 4), abc) 没有真正的扁平化,1,2不能迭代; 区别对待,偏函数 println(list.flatMap{ // //case x: Int => List(x) case y: Iterable[Int] => y //List(1, 2, 3, 4, abc),case顺序若是调换下就是List(1, 2, List(3, 4), abc)这个结果了 case a => List(a) })
再好比:
val map = Map("a"->1, "b"->2) map.foreach{ case(k, v) => { println(k + "=>" + v) } }
===>
a => 1
b => 2
将一段代码(从形式上看),做为参数传递给高阶函数,在高阶函数内部执行这段代码. 其使用的形式如 breakable{} 。
知足以下条件:
① 参数是函数
② 函数参数没有输入值也没有返回值
能够将一段代码逻辑做为参数传递给函数(方法),有利于功能的扩展
Breaks.breakable { val list = List(1, 2, 3, 4) for (i <- list) { if (i == 3) { Breaks.break() } } }
模仿源码:
class Breaks() extends scala.AnyRef { def breakable(op : => scala.Unit) : scala.Unit = { /* compiled code */ }
把一段逻辑写进来就能够写{ } 花括号了;
test{ val list = List(1, 2, 3, 4) for (i <- list) { println(i) } } def test(f: => Unit)={ f //f()函数能够调用,scala中要求f加括号了才能加,没有加()也不能加 }
for (){
} //for若是不是关键字, for()()柯里化,把一段逻辑写进来就能够写{ } 花括号了;
Scala提供try和catch块来处理异常。try块用于包含可能出错的代码。catch块用于处理try块中发生的异常。能够根据须要在程序中有任意数量的try...catch块。
语法处理上和Java相似,可是又不尽相同
Scala异常处理
ctrl+alt+t
/* Java异常处理回顾 try { // 可疑代码 val i = 0 val b = 10 val c = b / i // 执行代码时,会抛出ArithmeticException异常 } catch { case e: Exception => e.printStackTrace() } finally { // 最终要执行的代码 System.out.println("java finally") }*/
try { val r = 10 / 0 } catch { case ex: ArithmeticException => println("捕获了除数为零的算术异常") case ex: Exception => println("捕获了异常") } finally { // 最终要执行的代码 println("scala finally") }
1)咱们将可疑代码封装在try块中。 在try块以后使用了一个catch处理程序来捕获异常。若是发生任何异常,catch处理程序将处理它,程序将不会异常终止。
2)Scala的异常的工做机制和Java同样,可是Scala没有“checked(编译期或受检)”异常,即Scala没有编译异常这个概念,异常都是在运行的时候捕获处理。
3)用throw关键字,抛出一个异常对象。全部异常都是Throwable的子类型。throw表达式是有类型的,就是Nothing,由于Nothing是全部类型的子类型,因此throw表达式能够用在须要类型的地方
4)在Scala里,借用了模式匹配的思想来作异常的匹配,所以,在catch的代码里,是一系列case子句来匹配异常。当匹配上后 => 有多条语句能够换行写,相似 java 的 switch case x: 代码块..
5)异常捕捉的机制与其余语言中同样,若是有异常发生,catch子句是按次序捕捉的。所以,在catch子句中,越具体的异常越要靠前,越广泛的异常越靠后,若是把越广泛的异常写在前,把具体的异常写在后,在scala中也不会报错,但这样是很是很差的编程风格。
6)finally子句用于执行不论是正常处理仍是有异常发生时都须要执行的步骤,通常用于对象的清理工做,这点和Java同样。
7)Scala提供了throws关键字来声明异常。可使用方法定义声明异常。 它向调用者函数提供了此方法可能引起此异常的信息。 它有助于调用函数处理并将该代码包含在try-catch块中,以免程序异常终止。在scala中,可使用throws注释来声明异常