一、Scala语言介绍
Scala是一门多范式的编程语言,同时支持面向对象和面向函数编程风格。它以一种优雅的方式解决现实问题。虽然它是强静态类型的编程语言,但是它强大的类型推断能力,使其看起来就像是一个动态编程语言一样。Scala语言最终会被翻译成java字节码文件,可以无缝的和JVM集成,并且可以使用Scala调用java的代码库。除了Scala编程语言自身的特性以外,目前比较流行的Spark计算框架也是使用Scala语言编写。Spark 和 Scala 能够紧密集成,例如 使用Scala语言操作大数据集合的时候,用户可以像是在操作本地数据集那样简单操作Spark上的分布式数据集-RDD(这个概念是Spark 批处理的核心术语),继而简化大数据集的处理难度,简化开发步骤。
编程指南:https://docs.scala-lang.org/tour/tour-of-scala.html
二、环境配置
下载对应的scala版本:https://www.scala-lang.org/download/2.11.12.html
- Windows版本安装
1.scala-2.11.12.msi双击msi文件安装
2.配置Scala的环境变量SCALA_HOME变量
SCALA_HOME=C:\Program Files (x86)\scala
PATH=C:\Program Files\Java\jdk1.8.0_161/bin;C:\Program Files (x86)\scala/bin;
3.打开Window的cmd窗口
C:\Users\Administrator>scala
Welcome to Scala 2.11.12 (Java HotSpot™ 64-Bit Server VM, Java 1.8.0_161).
Type in expressions for evaluation. Or try :help.
scala> - CentOS安装
1.下载 scala-2.11.12.rpm
2.安装配置Scala-不需要配置环境变量
[root@CentOS ~]# rpm -ivh scala-2.11.12.rpm
Preparing… ########################################### [100%]
1:scala ########################################### [100%]
[root@CentOS ~]# scala
Welcome to Scala 2.11.12 (Java HotSpot™ 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.
scala>
- IDEA集成Scala开发环境
1.安装插件:在File>Setting>Plugins点击 install pluginfromdisk选项,选择 scala-intellij-bin-2018.2.11.zip安装成功后,重启IDEA。
2.实现创建Scala类
三、Scala API
(一)变量
Scala语言中没有原始数据类型,这一点和Java语言不同,在Scala中一切且对象。以下是Scala语言中常见类型和类型间的继承关系。
在Java中常见的基本类型在Scala中都被剔除了,Scala将值类型和引用类型分离。所有的数值变量类型都是** AnyVal**的子类,这些变量的值都有字面值。对于一些对象类型的变量都是 AnyRef的子类。对于 AnyRef类下的变量(除String类型),一般不允许直接赋值字面量,都需要借助 new关键创建。
1.变量声明
Scala语言是一种可以做类型自动推断的强类型的编程语言。变量的类型可通过编译器在编译的时候推断出最终类型。因此Scala中声明一个变量主需要告知编译器该变量的值是常量还是变量,可以省略类型(注意:并不意味之Scala是一种弱类型的编程语言,程序一旦编译后,类型固定,不可以更改)。例如:例如声明一个变量使用var即可,如果声明的是一个常量使用val关键字。因此Scala中变量声明的语法如下:
var|val 变量名[:类型] = 变量值[:类型]
例如:以下4中方式申明的变量结果均为i: Int = 1
方式1:var i:Int=1:Int
方式2:var i:Int=1
方式3:var i=1:Int
方式4:var i=1
2.数值转换
(1)类型兼容的数据转换-.asInstanceOf[]
var i=1--------------i: Int = 1
var b:Byte=i.asInstanceOf[Byte]--------------b: Byte = 1
(2)类型兼容或不兼容的数据转换-toInt、toChar…
var i=1--------------i: Int = 1
var b:Byte=i.toByte--------------b: Byte = 1
var s="true"--------------s: String = true
var sex=s.toBoolean--------------sex: Boolean = true
3.数组
定义数组
val a:Array[Int] = Array(1,2,3)
获取元素,可以通过length和size方法获取长度
a(0)
4.元组
元组是在Scala编程语言中的一种特殊变量,该变量可以组合任意几种类型的变量,构建一个只读的变量类型。访问元组的成员可以使用._元素下标访问。(一个元组最大允许22个元素)
定义元组
var t:(String,Int)=("zhangsan",18)----------t: (String, Int) = (zhangsan,18)
访问元组成员
t._1----------res01: String = zhangsan
t._2----------res02: Int = 18
5.分支循环
(1)条件分支-if
- 使用if可以作为条件分支控制语句,这一点类似与Java
var i:Int=100
if(i<10){
println("i小于10:"+i)
}else if(i<20){
println("i小于20:"+i)
}else{
print("i大于20:"+i)
}
- if修饰的代码分支可以赋值给一个变量(注:代码块中不允许使用return)
var i:Int=new Random().nextInt(30)
var result= if(i<10){
"小屁孩"
}else if(i<20){
true
}else{
("糟老头",1)
}
print(result)
6.while/do-while
while (i != 0){
i -= 1
println(i)
}
----------------
var i=5
do{
i -= 1
println(i)
}while(i!=0)
7.Break
Scala 语言中默认是没有 break 语句,但是在 Scala 2.8 版本后可以使用另外一种方式来实现 break 语句。当在循环中使用 break 语句,在执行到该语句时,就会中断循环并执行循环体之后的代码块。Scala 中 break 的语法有点不大一样,格式如下:
var loop=new Breaks//创建一个Breaks对象赋予loop变量
loop.breakable({ //loop.breakable把循环代码包起来,在需要break的条件中执行loop.break()
do{
i -= 1
print(i+"\t")
if(i==1) {
loop.break()
}
}while(i!=0)
})
8.for循环(重要)
(1)数组遍历-迭代
var array=Array(1,2,3,4)
for(item <- array){
print(item+"\t")
}
(2)数组遍历-下标
方式一:
var array:Array[Int]=Array(1,2,3,5,6)
for(i <- 0 to array.length-1){
print(array(i)+"\t")
}
方式二:
var array:Array[Int]=Array(1,2,3,5,6)
for(i <- 0 until array.length){
print(array(i)+"\t")
}
(3)多循环因子的for循环
for(i<- 1 to 9;j<-1 to i){
print(s"$i*$j="+(i*j)+"\t")
if(i==j){
println()
}
}
(4)嵌套if判断的for循环
for(i<- 1 to 9;if(i%2==0 && i%3==0)){
print(s"$i\t")
}
(5)关于数组计算的for循环
//定义1-9数字组成的数组
var array = 1 to 9
//遍历array数组,如果数字%2为0,则计算该数字的平方,并放入results数组中
val results=for(i<-array;if(i%2==0)) yield i*i
//遍历打印results数组数据
for(r<-results){
print(r+"\t")
}
9.Match-case模式匹配
模式匹配是检查某个值(value)是否匹配某一个模式的机制,一个成功的匹配同时会将匹配值解构为其
组成部分。它是Java中的 switch 语句的升级版,同样可以用于替代一系列的 if/else 语句。
(1)数值匹配
//获取0-10的随机整数
var x=new Random().nextInt(10)
var result= x match {
case 0 => "zero"
case 1 => "one"
case 2 => "two"
case 3 => "three"
case 4 => "four"
case _ => "other"//下划线代替default
}
print(result)
}
(2)类型匹配
//定义数据类型不同的数组
var array = Array("zhangsan",true,new Date(),15000.00)
//随机获取数组中的一个元素
var x =array(new Random().nextInt(array.length))
//进行类型匹配
var result= x match {
case a:String =>"name"
case a:Boolean =>"sex"
case a:Date =>"birth"
case a:Double =>"salary"
case _ =>""
}
print(result)
(二)函数
函数声明
def functionName ([参数列表]) : [return type] = {函数体}
1.标准函数
- 有返回值
方式一:
def sum(x:Int,y:Int): Int ={
return x+y
}
print(sum(1,2))
方式二:省略返回值类型和return
def sum(x:Int,y:Int)={
x+y
}
print(sum(1,2))
- 无返回值
def sum(x:Int,y:Int):Unit={
print(x+"\t"+y)
}
sum(1,2)
2.可变长参数
def sum(args:Int*):Int={
var sum:Int=0
//遍历可变长参数数组
for(arg<-args){
sum+=arg
}
return sum
}
print(sum(1,2,3,4))
3.有默认值参数
def sayHello(msg:String="Hello",name:String="zhangsan"): Unit ={
print(msg+"\t"+name)
}
sayHello()
4.命名参数
def sayHello(msg:String="Hello",name:String="zhangsan"): Unit ={
print(msg+"\t"+name)
}
sayHello(name="lisi",msg="nihao")
5.内嵌函数
//求阶乘-递归调用函数
def m1(x:Int)={
def m2(y:Int):Int={
if(y>0){
y*m2(y-1)
}else{
1
}
}
m2(x)
}
print(m1(5))
-------------------------
//等价方式
def m1(x:Int):Int={
if(x>0){
x*m1(x-1)
}else{
1
}
}
print(m1(5))
6.柯里化(Currying)
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数的新函数的这种技术。
//接受多个参数的函数
def sum(x:Int)(y:Int)(z:Int):Int={
x+y+z
}
//转变后的接受一个单一参数的新函数
def sum1:Int=>Int=sum(1)(2)(_)
print(sum1(5))
7.匿名函数(重点)
Scala 中定义的匿名函数的语法:箭头左边是参数列表,右边是函数体。
示例1:
//在本例中,匿名函数 (x:Int,y:Int)=>{x+y}返回值类型Int,充当变量m的值
var m:(Int,Int)=>Int= (x:Int,y:Int)=>{x+y}
//或: var m:(Int,Int)=>Int= (x,y)=>{x+y}
print(m(2,4))
示例2:
def main(args: Array[String]): Unit = {
print(method(1,2)(f))
}
var f =(x:Int,y:Int)=>{x+y}
//x、y、f相当于method的参数,变量f的值为匿名函数(x:Int,y:Int)=>{x+y}的返回值
def method(x:Int,y:Int)(f:(Int,Int)=>Int):Int={
f(x,y)
}
-----------------------------------------------------
//简化写法
def main(args: Array[String]): Unit = {
print(method(1,2)((v1,v2)=>v1+v2))
}
def method(x:Int,y:Int)(f:(Int,Int)=>Int):Int={
f(x,y)
}
(三)Class & object
1.单例类
由于Scala没有静态方法和静态类,通过object去定义静态方法或者静态对象。- 单例对象
方式一:
object User {
def sayHello(name:String):String={
"Hello ~"+name
}
}
-----------------------------------------
方式二:
object User {
def sayHello:(String)=>String = (name)=> "Hello ~"+name
}
}
2.类
class User {
var id:Int =_
var name:String = _
var age:Int = _
def this(age:Int){
this()
this.age=age
}
def this(id:Int,name:String,age:Int){
this(age)
this.name=name
this.id=id
}
}
_表示参数赋值为默认值。
使用该种方式声明构造类似于Java编程,但是要求在构造的第一行必须显示调用this方法。这本质上是因为如上声明的类,等价声明了class User(){}类似使用this()调用构造器。即
class User(var id:Int,var name:String,var age:Int) {
}
3.伴生对象
如果类和object在一个scala文件中,则称为object User 是class User的伴生对象,使用伴生对象可以方便的创建对象,只需要覆盖对应的apply方法,这里可以理解apply是一个工厂方法,该方法的作用是生产User实例对象。如下:
class User(id:Int,name:String,age:Int) {
override def toString: String = {
this.id+" "+this.name+" "+this.age
}
}
object User{
def apply(id: Int, name: String, age: Int): User = new User(id, name, age)
def apply(age:Int): User = new User(age)
}
---------------------
通过半生对象创建对象
var user=User(1,"zhangsan",18)//等价 new User(1,"zhangsan",18)
var user1=new User(18)//等价 new User(18)
使用unapply方法能够将对象中的一些属性反解出来
object User{
def unapply(user: User): Option[(Int,String,Int)] ={
Some(user.id,user.name,user.age)
}
}
---------------------
def main(args: Array[String]): Unit = {
var user=new User(1,"zhangsan",18)
//根据user对象获取其中的属性值
var User(id,name,age)=user
print(id+"\t"+name+"\t"+age)
}
注意一个伴生对象中只能有一个unapply方法,这不同于apply方法,因为apply方法可以有多个。
4.抽象类
abstract class Animal(name:String) {
def eat():Unit={
println("animal can eat...")
}
//抽象方法
def sleep():String
}
5.Trait(接口)
trait Speakable {
def speek():Unit//抽象方法
}
trait Flyable{
def fly():Unit//抽象方法
}
5.继承&实现
写一个Dog类继承Animal并且实现Speakable特质。
class Dog(name:String) extends Animal(name:String) with Speakable {
override def sleep(): String = {
"i'm a dog I sleep 8 hours"
}
override def eat(): Unit = {
println("啃骨头")
}
override def speek(): Unit = {
println("wang wang ~~")
}
}
object Dog{
def apply(name: String): Dog = new Dog(name)
}
①在覆盖有实现的方法必须添加override;②一个类只能继承一个类with多个trait例如:Class A extends B with C with D{}
6.Trait动态植入
假如说现在有一个Bird继承自Animal,在使用的时候会发现该类需要具备Flyable的功能,可以写法如下:
class Bird(name:String) extends Animal(name :String) {
override def sleep(): String = {
"bird is sleeping"
}
}
------------------------------------
var b=new Bird("麻雀") with Flyable{
override def fly(): Unit = {
println("小鸟会飞!")
}
}
b.fly()
7.self
等价于this关键字,在this出现混淆的时候使用self给this关键字起别名。
class User {
self =>//该类中使用self给this关键字起别名,放在所有属性之前
var id:Int = _
var name:String = _
var age:Int = _
def this( id:Int, name:String, age:Int){//此this代表有参构造,不能用self
this()//此this代表无参构造,不能用self
self.id=id
self.name=name
self.age=age
}
}
8.this强制混合
如下例:this:Bird=> 表明trait Flyable引入了Bird抽象类,FlyFish类想实现Flyable接口,也必须继承Bird类
trait Flyable{
this:Bird=>
}
abstract class Bird{
}
class FlyFish extends Bird with Flyable {
}
-------------------------------------------------------
//同样Bird是接口也如此
trait Flyable{
this:Bird=>
}
trait Bird{
}
class FlyFish extends Flyable with Bird{
}
9.Case class(样本类)
case class就像常规类,与普通的class不同的是,初始化的时候可以不用加new,Case class创建的对象 == 比较的是对象的内容。通常用于只读数据的建模。可以简单的使用copy来实现两个对象间的值得传递。
case class UserCase(id:Int,name:String){
}
-----------------------------------------
object Test {
def main(args: Array[String]): Unit = {
var u1=new UserCase(1,"zhangsan")
var u2=UserCase(1,"zhangsan")
var u3=u1.copy(2,u1.name)//copy实现对象之间值的传递
print(u1==u2)//结果为true。创建对象可不加new,==比较的是内容而不是地址
}
}
10.final用法
- 修饰类 ,表示最终类,该类无法被继承
- final修饰方法,表示方法不允许被覆盖
(abstract是否可以和final连用 Scala不关心) - final修饰属性,表示该属性不允许被子类遮盖,而在Java则表示常量。Scala使用val关键字表示属性不能被修改。
(四)可见性
1.Java和Scala访问控制符对比
2.this限定
表示去除伴生对象可见性,严格限定该属性只能在本类中或者子类中使用。
class Student(name:String) {
protected[this] var id=1
private[this] var sex=true
}
3.package限定
表示当前的类、属性、方法可以在指定的包下可见。
class Student(name:String) {
var id=1
private [demo01] var sex=true//demo01包中可以调用该属性
}
4.sealed(密封)
Trait和class、case class可以标记为 sealed,这意味着必须在同一文件中声明所有子类型这样就确保了所有的子类型都是已知的。
sealed case class Message(msg:String,code:Int)
class InfoMessage(msg:String,code:Int) extends Message(msg:String,code:Int)
class ErrorMessage(msg:String,code:Int) extends Message(msg:String,code:Int)
class WarnMessage(msg:String,code:Int) extends Message(msg:String,code:Int)
(五)函数对象
1.部分应用函数
在Scala中对所有的函数都可以理解为是一个接口函数。可以将任意一个函数转变成对象。
例如如下定义一个sum函数。
scala> def sum(v1:Int,v2:Int):Int={
| v1+v2
| }
sum: (v1: Int, v2: Int)Int
在Scala中可以尝试将一个函数转变成一个对象类型,如下:
scala> var sumFuction=sum _
sumFuction: (Int, Int) => Int = <function2>
通常将上述转得到的sumFuction称为sum函数的部分应用函数。
可以看出sumFunction是一个变量,该变量的类型是(Int, Int) => Int其中function2是该对象的字面值(参考变量var a:Int=1)。在Scala中所有的函数都可以更改成为Function1~22对象的实例。
例如在上例中如果只修改其中一个参数的值,得到sumForOne变量,function1就是该变量的字面值
scala> var sumForOne=sum(_:Int,2)
sumForOne: Int => Int = <function1>
scala> sumFuction.isInstanceOf[Function2[Int,Int,Int]]
res28: Boolean = true
可以看出sumFuction实际上Function2的一个实例而已,因此可以将以上函数定义为一个变量
scala> var sumFunction=new Function2[Int,Int,Int] {
| override def apply(v1: Int, v2: Int): Int = v1+v2
| }
sumFunction: (Int, Int) => Int = <function2>
在Scala中有一种更简洁的方法去描述Function1~22的简便写法,例如改写上诉sum函数
scala> var sumFunction:(Int,Int)=>Int = (x:Int,y:Int)=> x+y
sumFunction: (Int, Int) => Int = <function2>
不难看出上诉案例定义了一个变量sumFunction,该变量的类型是(Int, Int) => Int,该变量的实现是一个匿名函数( (x:Int,y:Int)=> x+y)。因为事实上(Int, Int) => Int就是一种函数类型。
scala> sumFunction.isInstanceOf[ (Int, Int) => Int]
res35: Boolean = true
scala> sumFunction.isInstanceOf[Function2[Int,Int,Int]]
res36: Boolean = true
进一步证实了(Int, Int) => Int就是Function2[Int,Int,Int]一种变体形式。
综上,可以得到结论以下几种方式是等价的:
方式一:
scala> def sum(x:Int,y:Int):Int={
| x+y
| }
scala>var sumFuction=sum _
方式二:
scala> class SumFunction extends Function2[Int,Int,Int] {
| override def apply(v1: Int, v2: Int): Int = {
| v1+v2
| }
| }
scala> var sumFunction=new SumFunction()
方式三:
scala> class SumFunction extends ((Int,Int)=>Int) {
| override def apply(v1: Int, v2: Int): Int = {
| v1+v2
| }
| }
scala> var sumFunction=new SumFunction()
方式四:
scala> var sumFunction:(Int,Int)=>Int = (x:Int,y:Int)=> x+y
sumFunction: (Int, Int) => Int = <function2>
2.偏函数-PartitalFunction
偏函数主要适用于处理指定类型的参数数据,通常用于集合处理中。定义一个函数,而让它只接受和处理其参数定义域范围内的子集,对于这个参数范围外的参数则抛出异常,这样的函数就是偏函数(顾名思异就是这个函数只处理传入来的部分参数)。偏函数是个特质其的类型为PartialFunction[A,B],其中接收一个类型为A的参数,返回一个类型为B的结果。
- 自定义偏函数类
class MyPartialFunction extends PartialFunction[Any, Int]{
override def isDefinedAt(x: Any): Boolean = {
x.isInstanceOf[Int]
}
override def apply(v1: Any): Int = {
v1.asInstanceOf[Int] * 2
}
}
object MyPartialFunction{
def main(args: Array[String]): Unit = {
var pf=new MyPartialFunction
print(pf(11))//打印22
print(pf("11"))//报错
}
}
- 类型匹配-1
object MyPartialFunction{
def main(args: Array[String]): Unit = {
var pf:PartialFunction[Any,Int]={
case x:Int => x*2
}
print(pf(11))//打印22
print(pf("11"))//报错
}
}
- 类型匹配-2
object TestMyImplicits {
def main(args: Array[String]): Unit = {
var a=Array(1,2,3,"a","b")
var b=a.collect({case x:Int => x*2})
println(b(0))//2
println(b(1))//4
println(b(2))//6
//数组b(2,4,6)
}
}
(六)异常处理
Scala 的异常处理和其它语言比如 Java 类似。Scala 的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。Scala 抛出异常的方法和 Java一样,使用 throw 方法,例如,抛出一个新的参数异常:
throw new ArithmeticException()
如果有异常发生,catch子句是按次序捕捉的。因此,在catch字句中,越具体的异常越要靠前,越普遍的异常越靠后。 如果抛出的异常不在catch子句中,该异常则无法处理,会被升级到调用者处。捕捉异常的catch子句,语法与其他语言中不太一样。在Scala里,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列case子句。
try {
//var i: Int = 10 / 0
throw new java.io.IOException()
} catch {
case e: ArithmeticException => {
println("e1:"+e.getMessage)
}
case e: Exception => {
println("e2:"+e.getMessage)
}
}
注意在Scala中并不会检查捕获异常的顺序,Scala并不区分运行异常和以检查异常。例如:
def main(args: Array[String]): Unit = {
throw new java.io.IOException()
}
finally 语句用于执行不管是正常处理还是有异常发生时都需要执行的步骤
(七)Scala导包
- 导入具体的一个包
import java.util.Date
- 导入特定的一个包下的几个类
import java.util.{Date, HashMap}
- 导入一个包下所有的类
import java.util._
- 当一个类中存在命名冲突,可以起别名
import scala.collection.mutable.HashMap
import java.util.{HashMap=>JHashMap}
val jmap = new JHashMap[String,Int]()
jmap.put("aa",1)
val map = HashMap(("aa",1))
println(map.get("aa").getOrElse(0))
println(jmap.get("aa"))
(八)隐式传值/隐式转换
1.隐式值注入\获取(参数、变量)
- 同一文件获取
implicit val i:Int=2 //用作隐式值注入-Int类型唯一
implicit val s:String="Hello" //用作隐式值注入-String类型唯一
val b1=implicitly[Int] //当前类文件中,只能有一个隐式值类型
val b2=implicitly[Int] //可以多次接收隐式值
val ss=implicitly[String] //当前类文件中,只能有一个隐式值类型
println("b:"+b+"\tss:"+ss)
- 不同文件获取
使用implicitly[类型],必须保证当前上下文有且仅有一个隐式值类型,一般这种隐式值变量的声明写在object单例类或者伴生对象中。例如:
package com.baizhi.demo12
object MyImplicits {
implicit val i:Int=2 //用作隐式值注入
implicit val s:String="Hello" //用作隐式值注入
}
import com.baizhi.demo12.MyImplicits._
val b=implicitly[Int] //当前类文件中,只能有一个隐式值类型
val ss=implicitly[String] //当前类文件中,只能有一个隐式值类型
println("b:"+b+"\tss:"+ss)
- 隐式注入值-同一文件-普通参数
object TestMyImplicits{
def main(args: Array[String]): Unit = {
implicit val m:String="你好"
sayHello(implicitly[String],"李四")
}
def sayHello(implicit msg:String,name:String): Unit ={
print(name+"\t"+msg)
}
}
注:当implicit放置在参数上时候,只能放在第一参数上。
- 隐式注入值-不同文件-柯里化参数
package com.baizhi.demo03
object MyImplicits {
implicit val s:String="你好"
}
object TestMyImplicits{
def main(args: Array[String]): Unit = {
import com.baizhi.demo03.MyImplicits._
sayHello("李四")//只提供不需要隐式注入的参数
}
def sayHello(name:String)(implicit msg:String): Unit ={//柯里化参数
print(name+"\t"+msg)
}
}
注:要求需要自动隐式值注入类型,必须放在其他参数列表后边
2.隐式参数转换(方法)
该方式是通过隐式转换将参数不满足的类型转为所需类型,Scala在编译代码的时候,先尝试正常编译如果发现编译类型不匹配,会尝试加载当前上下文中是否存在该类型和目标类型的一种隐式转换(这种转换严格意义上应该必须唯一,否则编译器会提示错误警告,并不影响执行),如果存在则编译通过。
package com.baizhi.demo03
import java.text.SimpleDateFormat
import java.util.Date
object MyImplicits {
implicit def strToDate(s:String):Date={
val sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf.parse(s)
}
}
import java.util.Date
object TestMyImplicits{
def main(args: Array[String]): Unit = {
import com.baizhi.demo03.MyImplicits._//引入隐式转换规则
getTime("2019-06-26 10:43:00")
/*getTime需要的参数类型为Date,此时传入的"2019-06-26 10:43:00"是字符
串不匹配,自动获得上下文的隐式转换规则进行字符串向日期类型的转换*/
}
def getTime(date:Date)={
println(date.getClass)//输出class java.util.Date
println(date.toLocaleString)//输出2019-6-26 10:43:00
}
}
3.隐式方法增强(类)
该方式是通过隐式转换将类的方法进行增强处理,当对目标对象调用不存在的方式时候,Scala会尝试加载当前上下文中是否存在该类的隐式增强类(这种转换严格意义上应该必须唯一,否则编译器会提示错误警告,并不影响执行),如果存在则编译通过。
package com.baizhi.demo03
//Pig类只有eat和sleep方法
class Pig(var name:String){
def eat()={
println(s"$name eat")
}
def sleep()={
println(s"$name sleep")
}
}
//PigImplicits类中为Pig类提供fly增强方法
object MyImplicits {
implicit class PigImplicits(pig:Pig){
def fly()={
println(pig.name+"会飞")
}
}
}
object TestMyImplicits{
def main(args: Array[String]): Unit = {
import com.baizhi.demo03.MyImplicits._//调用fly增强方法后导入生效
val pig=new Pig("乔治")
pig.eat()
pig.sleep()
pig.fly()//调用增强方法需要导入提供增强方法的类MyImplicits
}
}
4.Java集合转换为Scala
import java.util.ArrayList
import scala.collection.JavaConverters._//必须引入这个包
val array = new ArrayList[Int]()
array.add(1)
array.add(2)
array.add(3)
array.add(4)
//使用Java API实现累加
val sum = array.stream().reduce(new BinaryOperator[Int] {
override def apply(t: Int, u: Int): Int = {
t + u
}
}).get()
//通常引入JavaConverters之后,可以使用加强版本的集合操作
var total=array.asScala.sum
(九)Scala 泛型
1.<:
设置上边界
class Keeper[U <: Dog] {
def keep(u:U):Unit={
println(u)
}
}
表示只可以传递Dog或者Dog的子类
2. >:
设置下边界
class Keeper[U >: Dog] {
def keep(u:U):Unit={
println(u)
}
}
表示设置下边界, 泛型只允许设置Dog或者是Dog的父类
3.<%
视图限定
视图限定将会在后续版本移除,主要意思是指必须保证上下文中有能够提供一个隐式转换T <% U
能够将T隐式转为U类型,如下所示要求上下文中能够尝试将一个String类型隐式转换为Date类型。
package com.baizhi.demo03
import java.text.SimpleDateFormat
import java.util.Date
object MyPredefs {
implicit def strToDate(s:String):Date={
val sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf.parse(s)
}
}
import java.util.Date
object TestMyImplicits{
def main(args: Array[String]): Unit = {
import com.baizhi.demo03.MyPredefs._
getTime("2019-06-26 14:51:00")
}
def getTime[String <% Date](date:String)={/*此处加了视图限定后,date参数就可以调用.getTime方法,不加限定则不可调用*/
println(date.getTime)
}
}
4.T:A
上下文绑定
表示上下文中必须存在这种隐式值A[T]
隐式值,否则程序编译出错.这样可以在上下文中还没有隐式值得时候确保方法能编译成功。
object TestMyImplicits{
def main(args: Array[String]): Unit = {
import com.baizhi.demo03.MyPredefs._
sayInformation("你好")
}
def sayInformation[T:User](msg:T)={
var u=implicitly[User[T]]
u.sayHello(msg)
}
}
要求上下文中必须有一个隐式值User[T]类型,例如
package com.baizhi.demo03
object MyPredefs {
implicit val user=new User[String]()
}
class User[T]{
def sayHello(msg:T): Unit ={
println(msg)
}
}
参考:https://docs.scala-lang.org/tutorials/FAQ/context-bounds.html#what-is-a-context-bound
5.+A
协变
class Covariant[+T](t:T) {
}
val c1 = new Covariant[Dog](new Dog("小狗"))
val c2 = new Covariant[SmallDog](new SmallDog("小狗",true))
var c3:Covariant[SmallDog]= c1 //error
var c4:Covariant[Dog]= c2//父类引用指向子类对象(向下)
6.-A
逆变
class Covariant[-T](t:T) {
}
val c1 = new Covariant[Dog](new Dog("小狗"))
val c2 = new Covariant[SmallDog](new SmallDog("小狗",true))
var c3:Covariant[SmallDog]= c1//子类引用指向父类对象(向上)
var c4:Covariant[Dog]= c2 //error
7.A
不变
class Covariant[T](t:T) {
}
val c1 = new Covariant[Dog](new Dog("小狗"))
val c2 = new Covariant[SmallDog](new SmallDog("小狗",true))
var c3:Covariant[SmallDog]= c1//error
var c4:Covariant[Dog]= c2 //error
var c5:Covariant[Dog]= c1
var c6:Covariant[SmallDog]= c2
(十)数组/集合
(十一)常用集合算子
1.排序
- sorted-默认从小到大
def main(args: Array[String]): Unit = {
var array=Array(2,5,3,8,6,10)
for(a <- array.sorted){print(a+"\t")}
}
- sorted-从大到小
object TestMyImplicits{
def main(args: Array[String]): Unit = {
var o = new Ordering[Int] {
override def compare(x: Int, y: Int): Int = {
return x.compareTo(y) * -1
}
}
var array = Array(2, 5, 6, 8, 9, 10)
for (a <- array.sorted(o)) {
print(a + "\t")
}
}
}
- sortBy-默认从小到大
def main(args: Array[String]): Unit = {
var array = Array(("a",3),("g",6),("e",2),("f",9))
for (a <- array.sortBy(tuple=>tuple._1)) {
print(a + "\t")//(a,3) (e,2) (f,9) (g,6)
}
}
- sortBy-从大到小
object TestMyImplicits{
def main(args: Array[String]): Unit = {
implicit var o=new Ordering[String] {
override def compare(x: String, y: String): Int = {
x.compareTo(y) * -1
}
}
var array = Array(("a",3),("g",6),("e",2),("f",9))
for (a <- array.sortBy(tuple=>tuple._1)(identity(o))) {//identity(o)为柯里化参数
print(a + "\t")//(g,6) (f,9) (e,2) (a,3)
}
}
}
- sortWith
def main(args: Array[String]): Unit = {
var array = Array(("a",3),("g",6),("e",2),("f",9))
for (a <- array.sortWith((t1,t2)=>t1._2<t2._2)) {
print(a + "\t")//(e,2) (a,3) (g,6) (f,9)
}
}
//(t1,t2)=>t1._2>t2._2)为降序
2.flatten-铺开
def main(args: Array[String]): Unit = {
var list=List(Array("a","c"),Array("d","b"))
for (a <- list.flatten) {
print(a + "\t")//a c d b
}
}
3.map
def main(args: Array[String]): Unit = {
var list=List("Hello World","good good study","day day up")
var m=list.map(item=>item.split("\\s+"))//List(Array(hello, world), Array(good, good, study), Array(day, day, up))
for(a <- m.flatten){
print(a + "\t")//Hello World good good study day day up
}
}
3.flatMap
def main(args: Array[String]): Unit = {
var list=List("Hello World","good good study","day day up")
//var m=list.flatMap(item=>item.split("\\s+"))
var m=list.flatMap(_.split("\\s+"))//一个参数可以简写
for(a <- m){
print(a + "\t")
}
}
4.filter|filterNot
def main(args: Array[String]): Unit = {
var list=List("Hello World","good good study","day day up")
/* var m=list.flatMap(item=>item.split("\\s+"))
.filterNot(item=>item.equals("good"))*/
var m=list.flatMap(_.split("\\s+")).filterNot(_.equals("good"))//简写
for(a <- m){
print(a + "\t")//Hello World study day day up
}
}
4.groupBy
def main(args: Array[String]): Unit = {
var list=Array(("a",1),("b",2),("a",2))
var tuple=list.groupBy(x=>x._1).map(x=>(x._1,(for(i<-x._2) yield i._2).sum))
for(item <- tuple){
println(item._1+"\t"+item._2)
}
//b 2
//a 3
/*groupBy后的结果lst1: scala.collection.immutable.Map[String,Array[(String,
Int)]] = Map(b -> Array((b,2)), a -> Array((a,1), (a,2)))*/
}
5.fold
def main(args: Array[String]): Unit = {
var list=Array(Array(1,2,3),Array(1,2,4),Array(5,6,7),Array(9,4))
//先铺开,再计算,fold(0)中的0为计算初始值,分别与各个数字累加
//val sum = list.flatten.fold(0)((x,y)=>x+y)
val sum = list.flatten.fold(0)(_+_)//简写
print(sum)//44
}
6.aggregate(先分后总,效率高)
def main(args: Array[String]): Unit = {
var list=Array(Array(1,2,3),Array(1,2,4),Array(5,6,7),Array(9,4))
// 初始值 局部计算 逻辑汇总
//val sum = list.flatten.aggregate(0)((x,y)=>x+y,(x,y)=>x+y)
val sum = list.flatten.aggregate(0)(_+_,_+_)//简写
print(sum)
}
7.reduce
def main(args: Array[String]): Unit = {
var list=Array(Array(1,2,3),Array(1,2,4),Array(5,6,7),Array(9,4))
//val sum = list.flatten.reduce((a,b)=>a+b)
val sum = list.flatten.reduce(_+_)//简写
print(sum)
}
(十二)其他集合算子
1.group
scala> var lst=Array(1,2,4,5,6,7)
lst: Array[Int] = Array(1, 2, 4, 5, 6, 7)
scala> lst.grouped(3).toList
res48: List[Array[Int]] = List(Array(1, 2, 4), Array(5, 6, 7))
2.zip
scala> var v=List(1,2,4)
v: scala.collection.immutable.Vector[Int] = Vector(1, 2, 4)
scala> v.zip(Array("a","b","c"))
res52: scala.collection.immutable.Vector[(Int, String)] = Vector((1,a), (2,b), (4,c))
3.unzip
var v=List(("a",1),("b",2),("c",3))
v: List[(String, Int)] = List((a,1), (b,2), (c,3))
scala> v.unzip
res53: (List[String], List[Int]) = (List(a, b, c),List(1, 2, 3))
4.diff|intersect|union(差集|交集|并集)
//差集
scala> var v=List(1,2,3)
v: List[Int] = List(1, 2, 3)
scala> v.diff(List(2,3,5))
res54: List[Int] = List(1)
//交集
scala> var v=List(1,2,3,5)
v: List[Int] = List(1, 2, 3, 5)
scala> v.intersect(List(2,4,6))
res55: List[Int] = List(2)
//并集
scala> var v=List(1,2,3,5)
v: List[Int] = List(1, 2, 3, 5)
scala> v.union(List(2,4,6))
res56: List[Int] = List(1, 2, 3, 5, 2, 4, 6)
5.distinct-去重
scala> var v=List(1,2,3,3,5)
v: List[Int] = List(1, 2, 3, 3, 5)
scala> v.distinct
res57: List[Int] = List(1, 2, 3, 5)
6.Sliding-滑动集合
scala> var v=List(1,2,3,3,5)
v: List[Int] = List(1, 2, 3, 3, 5)
scala> v.sliding(3,1).toList
res60: List[List[Int]] = List(List(1, 2, 3), List(2, 3, 3), List(3, 3, 5))
(十三)综合案例
1.字符统计
scala> var arrs=Array("this is a demo","good good study","day day up")
arrs: Array[String] = Array(this is a demo, good good study, day day up)
scala> arrs.map(_.split(" ")).flatten.map((_,1)).groupBy(_._1).map(x=>(x._1,(for(i<- x._2) yield i._2.toInt).toList.sum)).toList.sortBy(_._1).foreach(println)
(a,1)
(day,2)
(demo,1)
(good,2)
(is,1)
(study,1)
(this,1)
(up,1)
2.文件字符
var source=Source.fromFile("D:\\demo\\words\\t_words")
var array=ArrayBuffer[String]()
val reader = source.bufferedReader()
var line = reader.readLine()
while(line!=null){
array+=line
line = reader.readLine()
}
array.flatMap(_.split(" "))
.map((_,1))
.groupBy(_._1)
.map(x=> (x._1,x._2.size))
.toList
.sortBy(_._1)
.foreach(println)
reader.close()