方法与函数
将方法生成一个新的函数
package cn.doit.com.day05.demo01
object MethodGenFunc {
def m(x: Int): Int = x*x
val f = (a: Int) => a * a
def main(args: Array[String]): Unit = {
val r1 = m(5)
val r2 = f.apply(5)
val arr = Array(1,2,3,4,5,6,7,8,9)
//传入函数 生成一个新的arr
val arr1: Array[Int] = arr.map(f)
//map方法中只能传入函数 这是一个语法糖,省略了_
//会生成一个新的函数,调用了原来的方法,逻辑相同
// val arr2: Array[Int] = arr.map(m)
val arr2: Array[Int] = arr.map(m _) //真正写法 m后面有个空格
println(arr2.toBuffer)
}
}
柯里化和隐式参数
package cn.doit.com.day05.demo2
object KeliDemo {
def m1(x:Int)(y:Int): Int = x + y
// def m2(x:Int , y:Int): Int = x + y
//普通方法可以在参数列表中指定默认值,一般是最后以后参数
//也可以每个参数都传默认值
def m2(x:Int , y:Int = 100): Int = x + y
def m3(x:Int = 100 , y:Int = 50): Int = x + y
//柯里化
def m4(x:Int)(y:Int = 100): Int = x + y
def m5(x:Int = 5)(y:Int = 100): Int = x + y
//隐式参数implicit 一般情况下是放在后面的参数里
def m6(x:Int)(implicit y:Int = 100): Int = x + y
//不是柯里化方法,但是有隐式参数,不一定只有柯里化方法中使用
def m7(implicit x:Int = 100): Int = x + 55
def main(args: Array[String]): Unit = {
// 105
// val r = m2(5)
//205 覆盖了默认值
val r = m2(5,200)
//覆盖了第一个参数
val a = m3(5)
/*
柯里化方法后面的括号传入的默认值,调用时也要指定多个括号,
有默认值的括号可以不传参数
*/
// 没传参使用默认值,()不能少
val b = m4(5)()
println(b)
// val b = m4(5) //编译时不报错 运行时报错 加了implicit就好了
//保证对应的个数就可以
val c = m5()()
println(c)
//如果隐式参数 方法定义在class中,先要创建这个类的实例,在用实例名称调用
// val clazz = new MyContextClass
// import clazz._
//将MyContext导进来
import MyContextObj.im //这是object中的,推荐使用
// implicit val abc = 661
//如果上下文中有多个参数类型一样的,编译器会不知道想要传入哪个儿报错
// implicit val abcd = 666
//隐式参数: 编译时会去程序的上下文找与自己类型一样,不在乎参数名是否相同,有就传入,覆盖默认值
val d = m6(5) //加了隐式参数传一个参数就不会报错
// val e = m6(5)(33) //手动传的优先级最高
println(d)
//会去m6的上文去找,在下面不会覆盖
// implicit val bcd = 777
val noKeli = m7
println(noKeli)
}
}
package cn.doit.com.day05.demo2
object MyContextObj {
implicit val im = 123456
}
class MyContextClass {
implicit val ip = 654321
}
RichFile案例
package cn.doit.com.day05.demo3
import scala.runtime.RichInt
object ToDemo {
def main(args: Array[String]): Unit = {
//Int 没有定义to方法,父类也没定义
//使用了隐式转化,使用RichInt中的To方法
1 to 10
//不使用隐式转换,显示的包装,要多敲好多次键盘
val r: Range.Inclusive = new RichInt(1) to (10)
//隐式转换, 隐式转换本质就是装饰(包装),背地里帮你包的
//scala默认就导入一些包 类 object. 隐式参数 方法 函数
// 1) import java.lang._ (117 types, 123 terms)
// 2) import scala._ (178 types, 172 terms)
// 3) import scala.Predef._ (123 terms, 62 are implicit)
//在 scala.LowPriorityImplicits 中有一个用import修饰的方法
//implicit def intWrapper(x:Int) =new runtime.RichInt(x)
//将Int 转成了 RichInt, 而RichInt中恰好定义了to方法
}
}
package cn.doit.com.day05.demo3
import java.io.File
import scala.io.Source
class RichFile(val file: File) {
def read(): String = {
//从文件中一次全部读取出来
Source.fromFile(file).mkString
}
}
object RichFile {
def main(args: Array[String]): Unit = {
val file = new File("D:\\a.txt")
// val content = file.read()
// val richFile = new RichFile(file)
// val content = richFile.read()
import MyPreDef._
val content = file.read()
println(content)
}
}
package cn.doit.com.day05.demo3
import java.io.File
object MyPreDef {
//隐式方法
implicit def fileToRichFile(file:File) = {
println("implicit method invoked")
new RichFile(file)
}
//隐式函数 隐式转换优先调用函数
implicit val file2RichFile = (file: File) => {
println("implicit func invoked")
new RichFile(file)
}
}
scala的泛型
package cn.doit.com.day05.demo04
/*
* [T <: Comparable[T]] 上界 upper bound java: <T extends Comparable>
* [T >: Comparable[T]] 下界 lower bound java: <T super Comparable>
* [T <% Comparable] 视图界定 view bound java: <T super Comparable>
* [T : Comparable] 上下文界定 context bound
* [-T] 逆变 方法输入参数
* [+T] 协变 方法返回
* */
class Pair[T <% Comparable[T]] {
def bigger(first:T, second: T): T ={
if (first.compareTo(second) > 0) first else second
}
}
object Pair {
def main(args: Array[String]): Unit = {
//Integer实现了Comparable接口
// val p = new Pair[Integer]
// val r = p.bigger(5, 10)
// println(r)
//如果使用T <% Comparable[T] 即视图界定,既不会报错,因为视图界定就是为了实现隐式转换的
//在Predef中有一个隐式转换方法 implicit def int2Integer(x: Int): java.lang.Integer = x.asInstanceOf[java.lang.Character]
val p2 = new Pair[Int]
//用implicit修饰的一个方法或函数 Int => Integer
//Int => Integer
val r2 = p2.bigger(5,10)
println(r2)
}
}
Ordering和Ordered 比较
Ordered 是对java 中comparable的一个扩展 , 又更加灵活的方法
Ordering 继承了compaartor 对compare方法进行了扩展
视图界定
<% 视图界定( view bound) 必须传一个隐式方法或函数
视图界定就是为了实现隐式转换 , 寻找对应的隐式转换方法
package cn.doit.com.day05.demo05
//Ordered中有 > < <= >= 等方法
//结论: 视图界定就是用来实现隐式转换的,需要一个隐式转换方法或函数
class MrRight[T <% Ordered[T]] {
def fanpaizi(first:T,second:T): T = {
if (first > second) first else second
}
}
object MrRight {
def main(args: Array[String]): Unit = {
import MyPreDef._
val mr = new MrRight[Boy]
val b1 = new Boy("zss",99)
val b2 = new Boy("lss",98)
val boy = mr.fanpaizi(b1, b2)
println(boy)
}
}
package cn.doit.com.day05.demo05
class Boy(val name:String, val fv:Int) /*extends Ordered[Boy]*/{
override def toString: String = s"Boy($name, $fv)"
/*override def compare(that: Boy): Int = {
this.fv - that.fv
}
*/
}
package cn.doit.com.day05.demo05
object MyPreDef {
//方法
implicit def boyToOrderedBoy(boy: Boy): Ordered[Boy] = new Ordered[Boy] {
override def compare(that: Boy): Int = {
println("implicit method invoked")
boy.fv - that.fv
}
}
//函数 函数优先级高
implicit val boy2ToOrderedBoy = (boy: Boy) => new Ordered[Boy] {
override def compare(that: Boy): Int = {
println("implicit func invoked")
boy.fv - that.fv
}
}
}
上下文界定
: 必须传一个隐式类型的参数
package cn.doit.com.day05.demo05
//上下文界定也是用来实现隐式转换的,但是必须有一个隐式转换的类型参数
class MissRight[T : Ordering] {
def choose(first:T , second:T):T = {
//把 T:Ordering 转化成了 Ordering[T]类型的
val ord: Ordering[T] = implicitly[Ordering[T]]
// gteq: x - y >= 0
if (ord.gteq(first,second)) first else second
}
}
object MissRight {
def main(args: Array[String]): Unit = {
import MyPreDef.OrderingGirl
val missr: MissRight[Girl] = new MissRight[Girl]
val g1 = new Girl("金莲",38)
val g2 = new Girl("吉泽老师",40)
val girl = missr.choose(g1, g2)
println(girl)
}
}
package cn.doit.com.day05.demo05
class Girl(val name:String , val fv:Double) {
//重写toString
override def toString = s"Girl($name, $fv)"
}
package cn.doit.com.day05.demo05
object MyPreDef {
//既不是方法也不是函数,是一个隐式的object
implicit object OrderingGirl extends Ordering[Girl] {
override def compare(x: Girl, y: Girl): Int = {
//Double实现了comparable接口
java.lang.Double.compare(x.fv , y.fv)
}
}
}
柯里化结合隐式
使用柯里化+隐式转换方法/函数 实现类似视图界定
使用柯里化+隐式参数 实现上下文界定
package cn.doit.com.day05.demo06
import cn.doit.com.day05.demo05.{Girl, MissRight, MyPreDef}
class Pair[T] {
//柯里化方法结合隐式转换方法或者函数 可以实现数图界定的功能
def select(first:T , second:T)(implicit f:T => Ordered[T]) = {
if(first > second) first else second
}
//柯里化方法结合隐式类型参数 可以实现上下文界定的功能
def choose(first:T , second:T)(implicit ord: Ordering[T]): T = {
if (ord.gteq(first,second)) first else second
}
}
object Pair {
def main(args: Array[String]): Unit = {
val pair = new Pair[Boy]
val a1 = new Boy("zss",18)
val a2 = new Boy("lss",28)
//使用柯里化+隐式转换方法或函数就可以实现类似视图界定的功能
import MyPreDef._
val boy = pair.select(a1, a2)
println(boy)
val pair2: Pair[Girl] = new Pair[Girl]
val g1 = new Girl("金莲",38)
val g2 = new Girl("吉泽老师",40)
import MyPreDef.OrderingGirl
val girl = pair2.choose(g1,g2)
println(girl)
}
}
package cn.doit.com.day05.demo06
import cn.doit.com.day05.demo05.Girl
object MyPreDef {
implicit def boyToOrderedBoy(boy: Boy): Ordered[Boy] = new Ordered[Boy] {
override def compare(that: Boy): Int = {
println("implicit method invoked")
boy.fv - that.fv
}
}
implicit val boy2ToOrderedBoy = (boy: Boy) => new Ordered[Boy] {
override def compare(that: Boy): Int = {
println("implicit func invoked")
boy.fv - that.fv
}
}
//既不是方法也不是函数,是一个隐式的object
implicit object OrderingGirl extends Ordering[Girl] {
override def compare(x: Girl, y: Girl): Int = {
//Double实现了comparable接口
java.lang.Double.compare(x.fv , y.fv)
}
}
}
package cn.doit.com.day05.demo06
class Boy(val name:String, val fv:Int){
override def toString: String = s"Boy($name, $fv)"
}
隐式转换就是想要对方法进行增强,对类进行增强或者扩展, 有两种方式
第一种是 对类进行包装,使用柯里化方法和隐式函数/方法来其进行增强 相当于视图界定
第二种 就是继承, 定义一个隐式的object继承某个类型也可以对其进行增强 相当于上下文界定
在java中扩展方法 :
1. 继承 继承后可以在子类中加方法,可以也重写方法来增强方法
2. 包装
3. 动态代理