1 Scala高级语法

1.1 柯里化(Currying)

柯里化(Currying)指的是将原来接受两个参数的方法变成新的接受一个参数的方法的过程。新的方法返回一个以原有第二个参数为参数的函数。

| // 我们看下这个方法的定义, 求2个数的和def add(x: Int, y: Int) = x + y// 那么我们应用的时候,应该是这样用:add(1,2)
// 现在我们把这个函数变一下形:
def add(x:Int)(y:Int) = x + y_// 那么我们应用的时候,应该是这样用:add(1)(2),最后结果都一样是3,这种方式(过程)就叫柯里化。经过柯里化之后,方法的通用性有所降低,但是适用性有所提高。

// 分析下其演变过程def add(x: Int) = (y: Int) => x + y
// (y: Int) => x + y 为一个匿名函数, 也就意味着add方法的返回值为一个匿名函数
// 那么现在的调用过程为val _result = add(2)val sum1 = result(3)val sum2 = result(8) | | —- |

1.2 隐式(implicit)详解

思考: 我们调用别人的框架,发现少了一些方法,需要添加,但是让别人为你一个人添加是不可能滴。
比如使用java.io.File读取文件非常的繁琐,能不能让Oracle公司给我们再添加一个read方法,直接返回文件中的所有内容??

1.2.1 隐式参数

隐式参数就用implicit 关键字修饰的参数,隐式参数初始化后,不需要显示的传入参数
如果说在当前环境(context上下文环境)找到了一个和隐式参数类型相同,且用implicit修饰的参数,那么默认的初始值就会被覆盖,而且可以不设置初始值。
上下文环境中,有且只能有一个满足条件的参数值,没有或匹配上多个会报错。
有多个隐式参数,也只能有一个implicit关键字,且要置于最后一个参数列表中。
只要有隐式参数,就只能出现在最后一个参数列表的位置。
def m1(x:Int)( implicit y:Int=10)(z:Int)=x + y // 编译错误

object ImplicitDemo {
implicit val abc = 12000
implicit val str= “hello”// implicit val str2= “hello”
def m1(x:Int)( implicit y:Int=10)=x + y
// 隐式参数只能放在最后一个参数列表中,m2方法是错误的
// def m2(x:Int)( implicit y:Int=10)(z:Int)= x + y + z
// 有多个隐式参数的定义方式
def m3(x:Int)(implicit y:String,z:Double=30d) ={ x + z }
def main(args: Array[String]): Unit = {
println(m1(1))// println(m3(10))
}
}

如果在其他的object中定义的隐式值,可以通过import 对象导入:

import ValuesDemo._

如果在其他的class中定义的隐式值,需要创建类的实例,然后 import实例对象.隐式值

val vc = new ValueClass()import vc.varparam

1.2.2 隐式转换

使用隐式转换将变量转换成预期的类型是编译器最先使用implicit的地方。当编译器看到类型X而却需要类型Y,它就在当前作用域查找是否定义了从类型X到类型Y的隐式定义。

// 使用to这个生成器的时候,会发现在Int类中,根本就没有to方法,实际调用的是RichInt方法val rg = 1.to(10)
println(rg)// 该类所在包的位置:scala.runtime.RichIntval rg2 = new RichInt(1).to(10)
println(s”richint : ${rg2})
/*
发生了什么??就是因为implicit 隐式转换
在调用Int的to方法时,发现没有,但是呢?在上下文context环境中,找到了一个implicit修饰的方法:
implicit def intWrapper(x: Int) = new runtime.RichInt(x)
该方法的输入参数是一个Int类型,返回值类型是RichInt类型,该类型中有to 方法
于是编译器,就调用了这个类型的to方法,并返回相应的结果
*/

该隐式方法定义在Predef对象中,该对象会被编译器默认引入。
在scala交互式命令行中,可使用:implicits -v 来查看默认导入的隐式转化。
part5-高级语法 - 图1

自定义示例:

val i: Int = 3.1415 // 此时程序会报错,因为声明的为Int类型
//添加如下代码:
implicit def double2Int(d: Double) = d.toInt // 在运行代码则没有错误,隐式方法val i: Int = 3.5 // 成功输出3

案例:现在我们就可以给File类添加一个read方法,返回一个文件中的所有内容。
方案1:使用装饰模式实现File类的read方法增强:

class RichFile(val file: File) {
// 使用scala.io.Source来读取文件, 并把结果拼接为字符串
def read() = Source.fromFile(file).mkString
}object RichFile {
def main(args: Array[String]): Unit = {
val file = new File(“hello.txt”)
// File类中并没有read方法来返回所有的内容
file.read()

// 使用装饰模式
val rf: RichFile = new RichFile(file)
val content = rf.read()
println(content) }
}

方案2:
定义一个RichFile类及伴生对象,在伴生对象中定义一个隐式方法(file -> RichFile),在伴生类中定义一个read方法并返回文件中的所有内容。

class RichFile(val file: File) {
// 使用scala.io.Source来读取文件, 并把结果拼接为字符串
def read() = Source.fromFile(file).mkString
}object RichFile {
def main(args: Array[String]): Unit = {
val file = new File(“hello.txt”)
// scala 隐式转换
implicit def file2RichFile(file:File):RichFile=new RichFile(file)
// 我们普通的一个File类,没有这样一个read方法,那么scala提供一种隐式转换的方式,
// 会在当前的上下文环境中,去找,有没有这样一个方法,把File转换为另一个类,
// 而且这个类中,有和read 方法签名一样的方法: 方法名,方法返回值类型,方法参数
val content:String= file.read()
println(content)
}
}

1.3 比较器Ordered和Ordering

回顾java中的比较器Comparable和Compartor
定义一个类,实现Comparable[T]接口,并重写compareTo方法。

public class Girl implements Comparable{
private String name ;
private int age;
public Girl(String name,int age){
this.name=name;
this.age=age;
}
… setter and getter …
@Override
public String toString() {
return “Girl{“ +”name=’” + name + ‘\‘’ +”, age=” + age +’}’;
}
@Override
public int compareTo(Girl o) {
return this.age - o.age;
}
}

测试:

public class TestGirl {
public static void main(String[] args) {
Girl g1 = new Girl(“yangmi”, 29);
Girl g2 = new Girl(“dongjie”, 18);
Girl g3 = new Girl(“fengjie”, 38);
ArrayList girls = new ArrayList<>();
girls.add(g1);
girls.add(g2);
girls.add(g3);
Collections.sort(girls); for(Girl g :girls){
System.out.println(g);
}
}
}

不实现接口,直接使用Compartor接口方法

public class TestGirl {
public static void main(String[] args) {
Girl g1 = new Girl(“yangmi”, 29);
Girl g2 = new Girl(“dongyue”, 18);
Girl g3 = new Girl(“fengjie”, 38);
ArrayList girls = new ArrayList<>();
girls.add(g1);
girls.add(g2);
girls.add(g3);
// 使用比较器
Collections.sort(girls, new Comparator() {
@Override
public int compare(Girl o1, Girl o2) {
return -(o1.getAge() - o2.getAge());
}
});
for(Girl g :girls){
System.out.println(g);
}
}
}

| // 在类上添加泛型class TestCompare[T <:Comparable[T]] {
// 自定义一个比较方法
def bigger(first: T, second: T): T = {
// if(first > second) first else second
// 要想使用compareTo方法进行比较,就必须传入实现了该方法的类,也就是 Comparable的子类
// 泛型中可以这么写 T <:Comparable[T] 就限制了比较的类型必须是Comparable的实现类了
if (first.compareTo(second) > 0) first else second
}
}object TestCompare extends App{

// 但是使用int测试会报错,主要是因为int类型并没有实现Comparable接口
val i = 1
val j = 10
_// string类型实现了Comparable接口
// val i:String = “hadoop”
// val j = “spark”

// 可使用包装类型Interger,该类实现了Comparable接口
val _compare = new TestCompare[Integer]
val res = compare.bigger(new Integer(i),new Integer(j))
println(res)
} | | —- |

1.4 类型约束

scala中也有类似的两个特质 Ordered 和 Ordering:
Ordered 相当于 java中的Comparable 功能更丰富 扩展了compare < > 等方法
Ordering 相当于 java中的Comparator 是一个比较器

<: upper bounds 上界 相当于给定一个最高标准 传入的类型必须是最高标准的子类或实现类 <? extends Comparable>>: lower bounds下界 相当于给定了一个最低标准 传入的类型必须是最低标准的父类或超类 <? super Comparable

1.4.1 上界(Upper Bounds)/下界(lower bounds)

  • Upper Bounds

在Java泛型里表示某个类型是Test类型的子类型,使用extends关键字:

//或用通配符的形式:
<? extends Test>
这种形式也叫upper bounds(上限或上界),同样的意思在Scala的写法为:
[T <: Test]
//或用通配符:
[_ <: Test]

  • Lower Bounds

在Java泛型里表示某个类型是Test类型的父类型,使用super关键字:

//或用通配符的形式:
<? super Test>
这种形式也叫lower bounds(下限或下界),同样的意思在scala的写法为:
[T >: Test]
//或用通配符:
[_ >: Test]

示例:

| // 定义一个class 实现了Ordered特质class BGirl(val name:String,val facVal:Int,val age :Int) extends Ordered[BGirl]{
// 重写compare方法
override def compare(that: BGirl): Int = {
// 比较 颜值相同,比较年龄 ; 如果颜值不同,就比较颜值
if(this.facVal == that.facVal){
this.age - that.age
}else{
this.facVal - that.facVal
}
}
}// 泛型限制了上界,比较的类型必须是Ordered的子类或者实现类class TestOrderedCompare[T <: Ordered[T]] {
// 定义一个比较方法 使用> 比较
def bigger(first: T, second: T): T = {
if(first > second) first else second
}
}
object TestBGirl extends App{// 普通的int类型没有实现Ordered特质,无法进行比较
val i:Int = 10
val j:Int = 20 // val com = new TestOrderedCompare[Int]
_// val result = compare.bigger(i,j)
// println(res.name)

// class BGirl 实现了Ordered特质,所以可以传入比较器中进行比较
private val _g1 = new BGirl(“ab”,95,30)
private val g2 = new BGirl(“hatono”,95,28)
val compare = new TestOrderedCompare[BGirl]
val res = compare.bigger(g1,g2)
println(res.name)
} | | —- |

1.4.2 视图界定/上下文界定

  • View bounds

<% 的意思是“view bounds”(视界),它比<:适用的范围更广,除了所有的子类型,还允许隐式转换过去的类型。隐式方法或者隐式函数
def method A <% B: R = …
等价于:
def method A(implicit viewAB: A => B): R = …
或等价于:
implicit def conver(a:A): B = …

示例:

class Boy(val name:String,val facVal :Int) {}class Pari[T <% Ordered[T]] {
// 定义一个比较方法
def bigger(first: T, second: T): T = {
if (first > second) first else second
}
}object Pari extends App {
val b1 = new Boy(“段子王”, 99)
val b2 = new Boy(“laozhao”, 999)
// Boy类没有实现Ordered,就不能进行比较
// 但是在类的定义中,使用视图界定 <% 就可以使用隐式方法 进行隐式转换
// 然后Boy类就可以正常比较了
implicit def man2OrderedMan2(man:Boy):Ordered[Boy]=new Ordered[Boy]{
// 排序的规则 按照颜值 升序
override def compare(that: Boy): Int = {
man.facVal - that.facVal
}
}
// 除了隐式方法,还可以使用隐式函数 来达到隐式转化的目的 二者选其一即可implicit val man2OrderedMan = (boy:Boy) => new Ordered[Boy]{
override def compare(that: Boy): Int = {
boy.facVal - that.facVal
}
}
val p1 = new Pari[Boy]
val bigger = p1.bigger(b1, b2)
print(bigger.name)
}
  • Context bounds

与view bounds一样context bounds(上下文界定)也是隐式参数的语法。上下文 界定 可以实现隐式转换,需要传入隐式值,实际上就是 一个隐式的object

| class Pari2[T: Ordering] {
def bigger(first: T, second: T): T = {
// 定义一个变量,实现了Ordering
val ord = implicitly[Ordering[T]]
if (ord.gt(first, second)) first else second
}
}object Pari2 extends App{
val b1 = new Boy(“段子王”,99)
val b2 = new Boy(“laozhao”,999)
_// 没有implicit Ordering 被定义 不能比较

// 定义一个隐式值
implicit object man2Ordering extends Ordering[Boy] {
override def compare(x: Boy, y: Boy): Int = {
x.facVal - y.facVal
}
}
// 在创建比较对象时,进行隐式转换
val _p = new Pari2[Boy]
val re = p.bigger(b1,b2)
println(re.name)
} | | —- |