Scala Programing Language
什么是Scala?
专门为计算而生的语言,Scala将(Java后者C++)面向对象设计和函数式编程
结合在一起的简洁的高级编程语言。而函数式编程强调的是通过传递算子(代码|函数)实现大规模数据集的本地计算。Scala虽然是一门独立的编程语言,但是它可以无缝和Java语言对接。Scala编译的代码可以直接运行在JVM之上。Spark 和 Scala 能够紧密集成,例如 使用Scala语言操作大数据集合的时候,用户可以像是在操作本地数据集那样简单操作Spark上的分布式数据集-RDD(这个概念是Spark 批处理的核心术语),继而简化大数据集的处理难度,简化开发步骤。
环境配置
下载对应的scala版本:https://www.scala-lang.org/download/2.11.12.html
Windows版本安装
- 点击 scala-2.11.12.msi双击msi文件安装
- 配置Scala的环境变量SCALA_HOME变量
SCALA_HOME=D:\Program Files\scala
同时在PATH变量中追加
xxx;%SCALA_HOME%\bin
- 打开控制台输入
scala
C:\Users\Administrator>java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)
C:\Users\Administrator>scala
Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_161).
Type in expressions for evaluation. Or try :help.
CentOS安装
- 下载 scala-2.11.12.rpm
- 安装配置Scala
[root@CentOSA ~]# rpm -ivh scala-2.11.12.rpm
Preparing... ########################################### [100%]
1:scala ########################################### [100%]
[root@CentOSA ~]# scala
Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_191).
Type in expressions for evaluation. Or try :help.
scala>
IDEA集成Scala开发环境
- 离线安装
在File>Setting>Plugins点击 install pluginfromdisk选项,选择 scala-intellij-bin-2018.2.11.zip安装成功后,重启IDEA。
- 在线安装(联网)
- 测试插件已经安装成功
变量
Scala语言中没有原始数据类型,这一点和Java语言不同,在Scala中一切且对象。以下是Scala语言中常见类型和类型间的继承关系。
在Java中常见的基本类型在Scala中都被剔除了,Scala将值类型和引用类型分离。所有的数值变量类型都是 AnyVal的子类,这些变量的值都有字面值。对于一些对象类型的变量都是 AnyRef的子类。对于 AnyRef类下的变量(除String类型),一般不允许直接赋值字面量,都需要借助 new关键创建。
变量声明
Scala语言是一种可以做类型自动推断的强类型的编程语言。变量的类型可通过编译器在编译的时候推断出最终类型。因此Scala中声明一个变量主需要告知编译器该变量的值是常量还是变量,例如:例如声明一个变量使用var即可,如果声明的是一个常量使用val关键字。因此Scala中变量声明的语法如下:
var|val 变量名[:类型] = 变量值[:类型]
例如:
scala> var i:Int=1:Int
i: Int = 1
scala> var j=1:Byte
j: Byte = 1
scala> var k=1
k: Int = 1
Scala所有的变量在声明时可以省略类型,并不意味之Scala是一种弱类型的编程语言,程序一旦编译后,类型固定,不可以更改。可以看出Scala在解释的代码的时候,可以根据字面量的值自动识别类型,这一点和Java是非常类似的,例如
scala> var b = true
b: Boolean = true
scala> var name = "zhangsan"
name: String = zhangsan
scala> var c='中'
c: Char = 中
scala> c = 97
c: Char = a
scala> print(c)
a
因此可以看出在使用Scala编程过程中,编译器是可以自动推断出
AnyVal
类型的变量,因此用户在声明AnyVal类型的变量的时候,无需指定变量类型。
数值转换
scala> var i=1
i: Int = 1
scala> var b:Byte=i.asInstanceOf[Byte]
b: Byte = 1
scala> var b:Byte=i.toByte()
<console>:12: error: Byte does not take parameters
var b:Byte=i.toByte()
^
scala> var b:Byte=i.toByte
b: Byte = 1
用户可以使用asInstanceOf[类型]这种方式在数值间转换。该方式只适合类型兼容的时候使用,如果类型不兼容可以使用toInt、toChar、…等可以将常规参数自动转换为数值类型。
scala> var sex="true"
sex: String = true
scala> sex.toBoolean
res2: Boolean = true
scala> sex.asInstanceOf[Boolean]
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean
at scala.runtime.BoxesRunTime.unboxToBoolean(BoxesRunTime.java:85)
... 32 elided
数组
Scala提供了简便的声明和使用方式,例如声明一个Int类型的数组,则使用如下:
scala> val array:Array[Int] = Array(1,2,3)
scala> array(0)
res10: Int = 1
可以通过length和size方法获取数组长度。
元组
元组是在Scala编程语言中的一种特殊变量,该变量可以组合任意几种类型的变量,构建一个只读
的变量类型。访问元组的成员可以使用_元素下标
访问。
scala> var t:(String,Int)=("zs",18)
t: (String, Int) = (zs,18)
scala> t._1
res10: String = zs
scala> t._2
res11: Int = 18
scala> t._1="lisi"
<console>:12: error: reassignment to val
t._1="lisi"
^
scala> var t=("zs",18,true,15000.0)
t: (String, Int, Boolean, Double) = (zs,18,true,15000.0)
一个元组最大允许有22个元素。
分支循环
条件分支-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修饰的代码分支可以赋值给一个变量
var i:Int=new Random().nextInt(30)
var result= if(i<10){
"小屁孩"
}else if(i<20){
true
}else{
("糟老头",1)
}
这里的代码块中不允许使用return
while/do-while
while (i != 0){
i -= 1
println(i)
}
var i=5
do{
i -= 1
println(i)
}while(i!=0)
Break
Scala 语言中默认是没有 break 语句,但是在 Scala 2.8 版本后可以使用另外一种方式来实现 break 语句。当在循环中使用 break 语句,在执行到该语句时,就会中断循环并执行循环体之后的代码块。Scala 中 break 的语法有点不大一样,格式如下:
var loop=new Breaks
loop.breakable({
do{
i -= 1
print(i+"\t")
if(i==1) {
loop.break()
}
}while(i!=0)
})
for循环(重点|难点)
var array=Array(1,2,3,4)
for(item <- array){
print(item+"\t")
}
var array=Array("a","b","c")
for( index <- 0 to array.length-1){
print(array(index))
}
var array=Array("a","b","c")
for( index <- 0 until array.length){
print(array(index)+"\t")
}
- for循环可以使用多个循环因子
for(i<-1 to 9;j <- 1 to i){
print(s"$i*$j="+(i*j)+"\t")
if(i==j) println()
}
- for循环可以嵌套if判断
for(i<-1 to 9;if(i%2==0 || i%3==0) ){
print(s"$i\t")
}
- 数组计算
var array=0 to 9
val results = for(i <- array;if(i%2==0)) yield i*i
for(i <- results){
println(i)
}
模式匹配
模式匹配是检查某个值(value)是否匹配某一个模式的机制,一个成功的匹配同时会将匹配值解构为其组成部分。它是Java中的switch
语句的升级版,同样可以用于替代一系列的 if/else 语句。
- 数值匹配
val x: Int = Random.nextInt(10)
var result= x match {
case 0 => "zero"
case 1 => "one"
case 2 => "two"
case 3 => "three"
case 4 => "four"
case _ => "other"
}
println(result)
- 类型匹配
var array=Array("zs",true,new Date(),1000.0)
var x=array(new Random().nextInt(array.length))
println("x:"+x)
var result= x match {
case v:String => "name"
case v:Boolean => "sex"
case x:Date => "birth"
case x:Double => "salary"
case _ => "未知"
}
println(result)
函数
函数声明
def functionName ([参数列表]) : [return type] = {
}
标准函数
def sum(x:Int,y:Int):Int={
return x+y
}
//可以尝试省略返回值类型
def multi(x:Int,y:Int)={
x*y
}
def sayHi(name:String):Unit={
println("hi~ "+name)
}
可变长参数
def sum(args:Int*):Int={
var sum:Int=0
for(agr <- args){
sum += agr
}
return sum
}
参数默认值
def sayHello(msg:String="hello",name:String="zhangsan"):Unit={
println(msg+" "+name)
}
sayHello()
命名参数
sayHello(name="zhangsan",msg="你好!")
def sayHello(msg:String="hello",name:String="zhangsan"):Unit={
println(msg+" "+name)
}
内嵌函数
def factorial(x:Int)={
def mulit(i:Int):Int={
if(i > 0){
i*mulit(i-1)
}else{
1
}
}
mulit(x)
}
等价
def factorial(x:Int):Int={
if(x>0){
x*factorial(x-1)
}else{
1
}
}
柯里化(Currying)
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数的新函数的这种技术。
def sum1(x:Int)(y:Int)(z:Int):Int={
x+y+z
}
scala> sum1(1)(2)(_)
res19: Int => Int = <function1>
scala> res19(3)
res20: Int = 6
未来用户只传递部分参数,返回新的函数
匿名函数(重点)
Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。使用匿名函数后,我们的代码变得更简洁了。
scala> (x:Int,y:Int) => {x+y}
res21: (Int, Int) => Int = <function2>
scala> res21(1,2)
res22: Int = 3
var f:(Int,Int)=>Int = (x:Int,y:Int)=>{x+y}
var f:(Int,Int)=>Int = (x,y)=>{x+y}
def method(x:Int,y:Int)(f:(Int,Int)=>Int):Int={
f(x,y)
}
var res=method(1,2)((v1:Int,v2:Int)=> v1+v2)
println(res)
函数和变量
- 定义的是变量- 函数变量
scala> var f:(Int,Int)=>Int = (x:Int,y:Int)=>{x+y}
f: (Int, Int) => Int = <function2>
- 声明函数变量
def sum:(Int,Int)=>Int = (x,y) => {x+y}
Class & object
单例类
由于Scala没有静态方法和静态类,通过object去定义静态方法或者静态对象。- 单例对象
object User {
def sayHello:(String)=>String = (name)=> "Hello ~"+name
def sayHello1(name:String):String={
"Hello ~"+name
}
}
var u1=User
var u2=User
println(u1 == u2) // true
伴生对象
当object和Class放在一个文件中时候,且名字保持一致称该object为当前Class的伴生对象。
class Animal {
}
object Animal{
}
类
class User {
var id:Int = _
var name:String= _
var birth:Date = _
def this(id:Int,name:String,birth:Date){
this()
this.id=id
this.name=name
this.birth=birth
}
override def toString: String = {
s"$id\t$name\t$birth"
}
}
var u=new User() //right
var u2=new User(1,"zs",new Date())
必须要求在构造方法的第一行显式调用this(),其中
_
表示参数赋值为默认值,因为Scala中class默认构造器是在类上声明的。
def this(参数)构造器重载
class User(var age:Int,var name:String) { //默认构造器
var birth:Date = _
def this(age:Int,name:String,birth:Date){
this(age,name)
this.birth=birth
}
override def toString: String = {
s"$age\t$name\t$birth"
}
}
var u1=new User() //error
var u2=new User(1,"zs")
var u3=new User(1,"zs",new Date())
使用该种方式声明构造类似于Java编程,但是要求在构造的第一行必须显示调用默认构造方法。
伴生对象创建类
- apply
使用伴生对象可以方便的创建对象,只需要覆盖对应的apply方法,如下:
class User(var age:Int,var name:String) {
var birth:Date = _
def this(age:Int,name:String,birth:Date){
this(age,name)
this.birth=birth
}
override def toString: String = {
s"$age\t$name\t$birth"
}
}
object User{
def apply(age: Int, name: String): User = new User(age, name)
def apply(age: Int, name: String,birth:Date): User = new User(age, name,birth)
}
var u3=User(1,"zs",new Date())
var u4=User(1,"zs",new Date())
例如在前期案例中声明数组就是使用半生对象方式创建的。
scala> var a=new Array[Int](3)
a: Array[Int] = Array(0, 0, 0)
scala> var b=Array(1,2,3)
b: Array[Int] = Array(1, 2, 3)
- unapply
用于将一个实体对象的属性解码出来。
class User(var age:Int,var name:String) {
var birth:Date = _
def this(age:Int,name:String,birth:Date){
this(age,name)
this.birth=birth
}
override def toString: String = {
s"$age\t$name\t$birth"
}
}
object User{
def apply(age: Int, name: String): User = new User(age, name)
def apply(age: Int, name: String,birth:Date): User = new User(age, name,birth)
def unapply(u: User): Option[(Int, String,Date)] ={
Some(u.age,u.name,u.birth)
}
}
val u = new User(1,"zs",new Date())
u.name="zhangsan"
var User(age,name,birth)=u
println(s"$age\t$name\t$birth")
抽象类
abstract class Animal(name:String) {
def eat():Unit={
println("animal can eat...")
}
def sleep():String
}
Trait/特质(接口)
trait Speakable {
def speek():Unit
}
trait Flyable {
def fly():Unit
}
继承&实现
class Bird(name:String) extends Animal(name:String) with Flyable with Speakable {
override def sleep(): String = {
s"$name 睡觉"
}
override def fly(): Unit = {
println(s"$name 会飞!")
}
override def speek(): Unit = {
println(s"$name 唧唧咋咋!")
}
}
class Dog(name:String) extends Animal(name:String) with Speakable {
override def sleep(): String = {
s"$name 睡觉!"
}
override def speek(): Unit = {
println(s"$name 旺旺叫!")
}
}
var d:Animal=new Dog("小黑")
var b:Animal=new Bird("小麻雀")
var d2:Dog=d.asInstanceOf[Dog]
d2.speek()
在scala中依然满足多态语意。
### 动态植入
class SmallBird(var name:String) extends Animal (name:String) with Speakable {
override def sleep(): String = {
s"$name 会睡觉!"
}
override def speek(): Unit = {
println(s"$name 咿咿呀呀!")
}
override def eat(): Unit = {
println(s"$name 吃鱼!")
}
}
var sb=new SmallBird("丑小鸭") with Flyable{
override def fly(): Unit = {
println(s"$name 会飞!")
}
}
sb.fly()
①在覆盖有实现的方法必须添加
overwrite
;
②一个类只能继承一个类(抽象)with多个trait例如:
class A extends B with C with D{} // B既可以是Trait也可以是Class,但是C/D必须是Trait
self
- 等价于this关键字,在this出现混淆的时候使用self给this关键字起别名。
class User(var name:String) {
self =>
def setName(name:String):Unit={
self.name=name
}
def getName():String={
self.name
}
}
- this强制混合
trait Flyable {
this:Bird =>
}
abstract class Bird {
}
class FlyFish extends Bird with Flyable {
}
Case class
case class就像常规类,case class适用于对不可变数据进行建模。
case class User(name:String,age:Int)
var u1= User("zs",19)
var u2= User("zs",19)
println(u1==u2) //true
var u1= new User("zs",19)
var u2= new User("zs",19)
println(u1==u2) //true
与普通的class不同的是,CaseClass创建的对象 == 比较的是对象的内容。通常用于只读数据的建模。可以简单的使用copy来实现两个对象间的值得传递
var u1= User("zs",19)
var u2= u1.copy(u1.name,20)
println(u2)
final用法
- 修饰类 ,表示最终类,该类无法被继承
- final修饰方法,表示方法不允许被覆盖
abstract是否可以和final连用 Scala不关心
- final修饰属性,表示改属性不允许被子类遮盖,而在Java则表示常量。因为Scala使用val关键字表示属性能否被修改。
可见性
Scala 中的默认可见性为 public,所谓默认即你没有在类或者成员前显示加 private 或 protected 可见性关键字。虽然默认可见性为 public,但这是逻辑上的,实际上 Scala 中并没有 public 这个关键字,如果你用 public 来声明一个类或成员,编译器会报错。
class User {
var id=1
var name="zhanngsan"
var age=18
var birthDay = new Date ()
}
在 Scala 中,可以在类型的 class 或 trait 关键字之前、字段的 val 或 var 之前,方法定义的 def 关键字之前指定可见性。如果不知道默认都是public。
Private
- 类
该类只能被同包下的子类继承,不同包类不可见,并且同包继承的类也必须是private修饰。因为这样才不会改变原始父类的可见性。被private修饰的类只能在同包下可用。
package com.baizhi.demo08
import java.util.Date
private class User {
var id=1
var name="zhanngsan"
var age=18
var birthDay = new Date ()
}
子类
package com.baizhi.demo08
private class SmallUser extends User {
}
- 属性|方法
在修饰属性表示该属性/方法只能被本类|伴生对象可见,其他均不可见。
package com.baizhi.demo08
import java.util.Date
private class User {
private var id=1
var name="zhanngsan"
var age=18
var birthDay = new Date ()
private def eat():Unit={
println("吃!")
}
}
object User{
def main(args: Array[String]): Unit = {
val user = new User()
user.eat()
user.id=2
}
}
Protected
- 修饰类
修饰类 该类可以被同包下的其他类访问。或者是同包子类使用。
package com.baizhi.demo09
protected class User {
}
package com.baizhi.demo09
class SmallUser extends User{
}
- 修饰属性和方法
被Protected修饰属性和方法只能在本类或者本类的子类以及半生对象中访问。
protected class User {
protected var name:String= _
protected def eat():Unit={
println("eat")
}
}
package com.baizhi.demo09
class SmallUser extends User{
}
object SmallUser{
def main(args: Array[String]): Unit = {
val sm = new SmallUser()
sm.eat()
}
}
this限定
可以去除伴生对象的可见性
class Student(name:String) {
var id=1
protected|private[this] var sex=true
def sayHello():Unit={
println(this.name)
}
}
object Student{
def apply(name: String): Student = new Student(name)
}
表示去除伴生对象可见性,严格限定该属性只能在本类中或者子类中使用。
package限定
表示当前的类、属性、方法可以在指定的包下可见。
class Student(name:String) {
var id=1
private [demo01] var sex=true
def sayHello():Unit={
println(this.name)
}
}
object Student{
def apply(name: String): Student = new Student(name)
}
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)
函数对象
在Java1.8中提出了一个Functional Interface,如果一个接口里面只能有一个抽象方法。这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。将该接口成为函数式接口。
@FunctionalInterface
public interface GreetingService {
void sayMessage(String message);
}
GreetingService gs=(message) -> System.out.println("hello "+message);
gs.sayMessage("张三");
Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。更多参考:https://www.cnblogs.com/chenpi/p/5890144.html
部分应用函数
在Scala中同样对所有的函数都可以理解为是一个接口函数。例如在Scala中可以将任意一个函数转变成对象。例如如下定义一个sum函数。
scala> def sum(v1:Int,v2:Int):Int={
| v1+v2
| }
sum: (v1: Int, v2: Int)Int
在Scala中可以尝试将一个函数转变成一个对象类型,如下:
scala> var sumFunction=sum _
sumFuction: (Int, Int) => Int = <function2>
这里的sumFunction
就是sum函数的部分应用函数。例如只修改其中一个参数的值:
scala> var sumForOne=sum(_:Int,2)
sumForOne: Int => Int = <function1>
可以看出sumFunction是一个变量,该变量的类型是(Int, Int) => Int
其中<function2>
是该对象的字面值。在Scala中左右的函数都可以更改成为Function1~22对象的实例。例如
var f1:Function2[Int,Int,Int]= (x,y)=>x +y
var f2:(Int,Int)=>Int = (x,y)=>x +y
f1.apply(1,2)
f1(1,2)
f2.apply(1,2)
f2(1,2)
通过以上的论证我们可以将
Function2[T1,T2,R] === (T1,T2)=>R
也就意味着(T1,T2)=>R
表示Function2类型。在Scala中有一种更简洁的方法去描述Function1~22的简便写法,例如改写上诉sum函数
scala> var sumFunction:(Int,Int)=>Int = (x:Int,y:Int)=> x+y
sumFunction: (Int, Int) => Int = <function2>
scala> sumFunction(1,2)
res34: Int = 3
scala> var f1:Function2[Int,Int,Int]= (x,y)=>x +y
f1: (Int, Int) => Int = <function2>
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
var pf2:PartialFunction[Any,Int]={
case x:Int => x*2
}
println( pf2("111"))
}
}
例如在Scala的数组中就有对片函数的使用:
var a=Array(1,2,3,"a","b")
a.collect({case x:Int => x*2})
for(item <- a;if(item.isInstanceOf[Int])) yield item.asInstanceOf[Int]*2
异常处理
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 语句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,实例如下:
try {
println("--------")
var i=10/0
println("++++++++")
} catch {
case e:Exception =>{
println("e:"+e.getMessage)
}
} finally {
println("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"))
隐式传值\隐式转换
隐式值获取
implicit val i:Int=2 //用作隐式值注入
implicit val s:String="Hello" //用作隐式值注入
val b=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)
隐式注入值
当implicit
放置在参数上时候,只能放在第一参数上。
def sayHello(implicit msg:String,name:String):Unit={
println(msg+" "+name)
}
import com.baizhi.demo12.MyImplicits._
sayHello(implicitly[String],"zhansgan")
由此可以看出,这种写法不够简洁,Scala推荐大家如果使用隐式值注入,一般使用柯里化风格
def sayHello(name:String)(implicit msg:String):Unit={
println(msg+" "+name)
}
import com.baizhi.demo12.MyImplicits._
sayHello("zhansgan")
要求需要自动值注入类型,必须选在最后面
隐式参数转换
该方式是通过隐式转换将参数不满足的类型转为所需类型,Scala在编译代码的时候,先尝试正常编译如果发现编译类型不匹配,会尝试加载当前上下文中是否存在该类型和目标类型的一种隐式转换(这种转换严格意义上应该必须唯一,否则编译器会提示错误警告,并不影响执行),如果存在则编译通过。
object MyImplicits {
implicit def str2Date(s:String):Date={
val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf.parse(s)
}
}
def tellTime(date:Date):Unit={
println(date.toLocaleString)
}
tellTime(new Date())
tellTime("2019-06-26 10:43:00")
隐式方法增强
该方式是通过隐式转换将类的方法进行增强处理,当对目标对象调用不存在的方式时候,Scala会尝试加载当前上下文中是否存在该类的隐式增强类(这种转换严格意义上应该必须唯一,否则编译器会提示错误警告,并不影响执行),如果存在则编译通过。
object MyImplicits {
implicit class PigImplicits(pig:Pig){
def fly():Unit={
println(pig.name+" 会飞!")
}
}
}
class Pig(var name:String) {
def eat():Unit={
println(s"$name eat")
}
def sleep():Unit={
println(s"$name sleep")
}
}
import com.baizhi.demo12.MyImplicits._
val pig = new Pig("佩奇")
pig.eat()
pig.sleep()
pig.fly()
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 泛型
<:
设置上边界
class Keeper[U <: Dog] {
def keep(u:U):Unit={
println(u)
}
}
表示只可以传递Dog或者Dog的子类
>:
设置下边界
class Keeper[U >: Dog] {
def keep(u:U):Unit={
println(u)
}
}
表示设置下边界, 泛型只允许设置Dog或者是Dog的父类
<%
视图限定
视图限定将会在后续版本移除,主要意思是指必须保证上下文中有能够提供一个隐式转换T <% U
能够将T隐式转为U类型,如下所示要求上下文中能够尝试将一个String类型隐式转换为Date类型。
def sayTime[String <% Date](date:String):Unit={
println(date.getTime)
}
import com.baizhi.demo13.MyPredefs._
sayTime("2019-06-26 14:51:00")
T:A
上下文绑定
表示上下文中必须存在这种隐式值A[T]
隐式值,否则程序编译出错.这样可以在上下文中还没有隐式值得时候确保方法能编译成功。
implicit val u1=new User[String]()
def sayInfomation[T:User](msg:T):Unit={
var u=implicitly[User[T]]
u.sayHello(msg)
}
要求上下文中必须有一个隐式值User[T]类型,例如
implicit val u1=new User[String]()
def sayInfomation[T](msg:T):Unit={
var u=implicitly[User[T]] //无法获取上下文的隐式值
u.sayHello(msg)
}
class User[T] {
def sayHello(t:T): Unit ={
println(t)
}
}
参考:https://docs.scala-lang.org/tutorials/FAQ/context-bounds.html#what-is-a-context-bound
+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
-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
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
集合/数组(重点)
Array
Array在Scala中指的是数组,创建方式如下所示,可以看出分配大小为10,每个元素都分配了默认值。
scala> var array=new Array[Int](10)
array: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
修改指定位置元素
scala> array(0)=1
scala> array
res57: Array[Int] = Array(1, 0, 0, 0, 0, 0, 0, 0, 0, 0)
scala> array.update(1,2)
scala> array
res62: Array[Int] = Array(1, 2, 0, 0, 0, 0, 0, 0, 0, 0)
获取数组长度
scala> array.length
res63: Int = 10
scala> array.size
res64: Int = 10
数组去重
scala> array
res66: Array[Int] = Array(1, 2, 0, 0, 0, 0, 0, 0, 0, 0)
scala> array.distinct
res67: Array[Int] = Array(1, 2, 0)
Range
改元素指定在创建的时候指定步长,相比较于Array,该Range只读,不允许修改。
scala> var range= new Range(0,10,2)
range: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)
scala> var range= 0 to 10 by 3
range: scala.collection.immutable.Range = Range(0, 3, 6, 9)
scala> var range=0 until 10 by 2
range: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)
Vector
按照下标顺序排列的向量,向量不支持修改。
scala> var vec=Vector(0,10,1)
vec: scala.collection.immutable.Vector[Int] = Vector(0, 10, 1)
scala> var vector=for(i<- 0 to 10 by 2) yield i
vector: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 2, 4, 6, 8, 10)
Iterator
scala> var it=Iterator(1,2,3)
it: Iterator[Int] = non-empty iterator
scala> it.foreach(println)
1
2
3
scala> it.isEmpty
res41: Boolean = true
List
:: / +: 追加元素
scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)
scala> list::=10
scala> list
res84: List[Int] = List(10, 1, 2, 3)
scala> list(0)=11
<console>:13: error: value update is not a member of List[Int]
list(0)=11
^
scala> list +:= -1
scala> list
res87: List[Int] = List(-1, 10, 1, 2, 3)
:::合并集合
scala> var list=List(1,2,3)
scala> var prefix=List(4,5,6)
scala> list :::= (prefix) //在list元素之前追加prefix元素,生成新的集合
scala> list
res32: List[Int] = List(4, 5, 6, 1, 2, 3)
drop/dropRight
scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)
scala> list.drop(2) //删除前n个元素
res30: List[Int] = List(3)
slice
scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)
scala> list.slice(1,3)
res104: List[Int] = List(2, 3)
reverse
scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)
scala> list.reverse
res105: List[Int] = List(3, 2, 1)
take/takeRight
scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)
scala> list.take(2)
res106: List[Int] = List(1, 2)
scala> list.takeRight(2)
res107: List[Int] = List(2, 3)
takeWhile
scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)
scala> list.takeWhile(x=> x<3 )
res108: List[Int] = List(1, 2)
scala> list.takeWhile(x=> x%2==1 )
res110: List[Int] = List(1)
head/last/tail
scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)
scala> list.head
res111: Int = 1
scala> list.tail
res112: List[Int] = List(2, 3)
scala> list.last
res113: Int = 3
ListBuffer
update
scala> import scala.collection.mutable._
import scala.collection.mutable._
scala> var list=ListBuffer[Int]()
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer()
scala> var list=ListBuffer(1,2,3)
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)
scala> list.update(0,10)
scala> list
res1: scala.collection.mutable.ListBuffer[Int] = ListBuffer(10, 2, 3)
+=/+=:/++=/++=:
添加删除元素
scala> var list=ListBuffer(1,2,3)
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)
scala> list.+=(4)
res12: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4)
scala> list.+=:(5)
res13: scala.collection.mutable.ListBuffer[Int] = ListBuffer(5, 1, 2, 3, 4)
scala> list.++=(List(6,7,8))
res14: scala.collection.mutable.ListBuffer[Int] = ListBuffer(5, 1, 2, 3, 4, 6, 7, 8)
+=
集合末尾添加元素,+=:
集合开始添加元素,++=
追加集合在尾部,++=:
在元素尾部添加元素
-/-=
scala> list.-(1)
res18: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3, 1)
scala> list
res19: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 1)
scala> var list=ListBuffer(1,2,3,1)
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 1)
scala> list.-=(1)
res20: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3, 1)
scala> list
res21: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3, 1)
区别
-
表示创建新的集合但是不包含删除的这个元素,-=
表示删除集合指定元素,更新原始集合
remove
删除指定位置元素
scala> var list=ListBuffer(1,2,3)
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)
scala> list.remove(0)
res25: Int = 1
scala> list
res26: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3)
insert/insertAll
在指定位置之前插入元素
scala> list
res27: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3)
scala> list.insert(0,4)
scala> list
res29: scala.collection.mutable.ListBuffer[Int] = ListBuffer(4, 2, 3)
scala> list.insertAll(2,List(1,2,3))
scala> list
res31: scala.collection.mutable.ListBuffer[Int] = ListBuffer(4, 2, 1, 2, 3, 3)
prependToList
将list1结果添加到list2中,
scala> var list1=List(1,2,3)
list1: List[Int] = List(1, 2, 3)
scala> var list2=ListBuffer(0,0)
list2: scala.collection.mutable.ListBuffer[Int] = ListBuffer(0, 0)
scala> list2.prependToList(list1)
res34: List[Int] = List(0, 0, 1, 2, 3)
scala> list2
res36: scala.collection.mutable.ListBuffer[Int] = ListBuffer(0, 0, 1, 2, 3)
Set(不可修改)
+/+=|-/-=
scala> var s=Set[Int](1,2,4,6,2)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 4, 6)
scala> s.+=(1)
scala> s
s: scala.collection.immutable.Set[Int] = Set(1, 2, 4, 6)
scala> s+(7)
res12: scala.collection.immutable.Set[Int] = Set(1, 6, 2, 7, 4)
区别是
+
创建新的Set并添加元素,不修改原始set,+=
修改原始set,类似-、-=就不在阐述
size
scala> var s=Set[Int](1,2,4,6,2)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 4, 6)
scala> s.size
res23: Int = 4
Set(可修改)
+/+=/-/-=
scala> var s=scala.collection.mutable.Set(1,2,3)
s: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
scala> s.+(4)
res25: scala.collection.mutable.Set[Int] = Set(1, 2, 3, 4)
scala> s
res26: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
scala> s.+=(4)
res27: scala.collection.mutable.Set[Int] = Set(1, 2, 3, 4)
scala> s
res28: scala.collection.mutable.Set[Int] = Set(1, 2, 3, 4)
scala> s.-(2)
res29: scala.collection.mutable.Set[Int] = Set(1, 3, 4)
scala> s
res30: scala.collection.mutable.Set[Int] = Set(1, 2, 3, 4)
scala> s.-=(2)
res31: scala.collection.mutable.Set[Int] = Set(1, 3, 4)
scala> s
res32: scala.collection.mutable.Set[Int] = Set(1, 3, 4)
remove/add
scala> var s=scala.collection.mutable.Set(1,2,3)
s: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
scala> s.remove(2)
res37: Boolean = true
scala> s.remove(-1)
res38: Boolean = false
scala> s
res39: scala.collection.mutable.Set[Int] = Set(1, 3)
scala> s.add(4)
res41: Boolean = true
HashMap(不可变)
+/+=/-/-=
scala> import scala.collection.immutable.HashMap
import scala.collection.immutable.HashMap
scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)
scala> hm+="工商"->"003"
scala> hm.+=(("工商1","004"))
scala> hm
res4: scala.collection.immutable.HashMap[String,String] = Map(工商 -> 003, 建设 -> 001, 招商 -> 002, 工商1 -> 004)
scala> hm.-("工商")
res6: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002, 工商1 -> 004)
scala> hm
res7: scala.collection.immutable.HashMap[String,String] = Map(工商 -> 003, 建设 -> 001, 招商 -> 002, 工商1 -> 004)
scala> hm.-=("工商")
scala> hm
res9: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002, 工商1 -> 004)
get
scala> import scala.collection.immutable.HashMap
import scala.collection.immutable.HashMap
scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)
scala> hm.get("招商").getOrElse("没有值")
res11: String = 002
scala> hm.get("招商1").getOrElse("没有值")
res12: String = 没有值
size
scala> hm
res16: scala.collection.immutable.HashMap[String,String] = Map(工商 -> 003, 建设 -> 001, 招商 -> 002, 工商1 -> 004)
scala> for(i<- hm) println(i._1+" -> "+i._2)
工商 -> 003
建设 -> 001
招商 -> 002
工商1 -> 004
merged
scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)
scala> var newhm=HashMap[String,String](("邮政","004"))
newhm: scala.collection.immutable.HashMap[String,String] = Map(邮政 ->
scala> var mergerFunction:((String,String),(String,String))=>(String,String) = (tuple1,tuple2)=>{
|
| if(tuple1._1.equals(tuple2._1)){
| (tuple1._1,tuple2._2)
| }else{
| tuple1
| }
| }
scala> var res=hm.merged(newhm)(mergerFunction)
res: scala.collection.immutable.HashMap[String,String] = Map(邮政 -> 004, 建设 -> 001, 招商 -> 002)
scala> res
res18: scala.collection.immutable.HashMap[String,String] = Map(邮政 -> 004, 建设 -> 001, 招商 -> 002)
scala> for(i<- res) println(i)
(邮政,004)
(建设,001)
(招商,002)
keys/values
scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)
scala> hm.keys
res23: Iterable[String] = Set(建设, 招商)
scala> hm.values
res24: Iterable[String] = MapLike(001, 002)
HashMap(可变)
remove/put
scala> import scala.collection.mutable.HashMap
import scala.collection.mutable.HashMap
scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.mutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002
scala> hm+="工商"->"003"
res0: scala.collection.mutable.HashMap[String,String] = Map(工商 -> 003, 建设 -> 001, 招商 -> 002)
scala> hm-=("工商")
res1: scala.collection.mutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)
scala> hm.put("邮政","004")
res2: Option[String] = None
scala> hm.remove("工商")
res3: Option[String] = None
scala> for(i<- hm) println(i)
(建设,001)
(邮政,004)
(招商,002)
集合算子(重点)
排序 Sort(如果集合内元素为map排序前需转换成元组)
- sorted
def sorted[B >: Int](implicit ord: scala.math.Ordering[B]): Array[Int]
scala> var array=Array(1,2,4,5,3)
array: Array[Int] = Array(1, 2, 4, 5, 3)
scala> list.sorted
res4: List[Int] = List(1, 2, 3, 4, 5)
- sortBy
def sortBy[B](f: (String,Int) => B)(implicit ord: scala.math.Ordering[B]): List[(String,Int)]
scala> var list=Array(("a",1),("b",2),("d",5),("c",4))
list: Array[(String, Int)] = Array((a,1), (b,2), (d,5), (c,4))
scala> list.sortBy(t=>t._2) // list.sortBy(_._2)
res7: Array[(String, Int)] = Array((a,1), (b,2), (c,4), (d,5))
scala> list.sortBy(t=>t._1) //list.sortBy(_._2)
res8: Array[(String, Int)] = Array((a,1), (b,2), (c,4), (d,5))
- sortWith
用户可以指定多个Field做联合排序
def sortWith(lt: ((String, Int), (String, Int)) => Boolean): Array[(String, Int)]
scala> var list=Array(("a",2),("a",1),("b",2),("d",5),("c",4))
list: Array[(String, Int)] = Array((a,2), (a,1), (b,2), (d,5), (c,4))
scala> val tuples = list.sortWith((t1, t2) => {
| if (!t1._1.equals(t2._1)) {
| t1._1.compareTo(t2._1) < 0
| } else {
//第一个排序规则失效时
| t1._2.compareTo(t2._2) < 0
| }
| })
tuples: Array[(String, Int)] = Array((a,1), (a,2), (b,2), (c,4), (d,5))
flatten(拉平)
scala> var list=Array(Array(1,2,3),Array(4,5))
scala> list.flatten
res6: Array[Int] = Array(1, 2, 3, 4, 5)
map(转换)
scala> var list=Array("this is a demo","hello word")
list: Array[String] = Array(this is a demo, hello word)
scala> list.map(line=>line.split(" ")) // 等价list.map(_.split(" "))
res13: Array[Array[String]] = Array(Array(this, is, a, demo), Array(hello, word))
小综合:
scala> list.map(_.split(" ")).flatten.map((_,1))
res14: Array[(String, Int)] = Array((this,1), (is,1), (a,1), (demo,1), (hello,1), (word,1))
flatMap
scala> var list=Array("this is a demo","hello word")
list: Array[String] = Array(this is a demo, hello word)
scala> list.flatMap(_.split(" "))
res16: Array[String] = Array(this, is, a, demo, hello, word)
scala> list.flatMap(_.split(" ")).map((_,1))
res17: Array[(String, Int)] = Array((this,1), (is,1), (a,1), (demo,1), (hello,1)
, (word,1))
scala> list.flatMap(line=> (for(i<-line.split(" ")) yield (i,1)))
res20: Array[(String, Int)] = Array((this,1), (is,1), (a,1), (demo,1), (hello,1)
, (word,1))
filter/filterNot
scala> var list=Array("this is a demo","hello word")
list: Array[String] = Array(this is a demo, hello word)
scala> list.filter(line=>line.contains("hello"))
res24: Array[String] = Array(hello word)
scala> list.filterNot(line=>line.contains("hello"))
res25: Array[String] = Array(this is a demo)
groupBy
scala> var list=Array("good","good","study")
list: Array[String] = Array(good, good, study)
scala> list.groupBy(item=>item)
res30: scala.collection.immutable.Map[String,Array[String]] = Map(study -> Array
(study), good -> Array(good, good))
scala> list.groupBy(item=>item).map(t=>(t._1,t._2.size))
res34: scala.collection.immutable.Map[String,Int] = Map(study -> 1, good -> 2)
fold
scala> var lst=Array(1, 2, 3, 1, 2, 4, 5, 6, 7, 9, 4)
lst: Array[Int] = Array(1, 2, 3, 1, 2, 4, 5, 6, 7, 9, 4)
scala> lst.fold[Int](0)((v1,v2)=>v1+v2) // lst.fold(0)(_+_)
res52: Int = 44
aggregate
scala> var lst=Array(1, 2, 3, 1, 2, 4, 5, 6, 7, 9, 4)
lst: Array[Int] = Array(1, 2, 3, 1, 2, 4, 5, 6, 7, 9, 4)
scala> lst.aggregate
def aggregate[B](z: => B)(seqop: (B, Int) => B,combop: (B, B) => B): B
scala> lst.aggregate(0)((v1,v2)=>v1+v2,(b1,b2)=>b1+b2) //lst.aggregate(0)(_+_,_+_)
res53: Int = 44
reduce
scala> var lst=Array(1, 2, 3, 1, 2, 4, 5, 6, 7, 9, 4)
lst: Array[Int] = Array(1, 2, 3, 1, 2, 4, 5, 6, 7, 9, 4)
scala> lst.reduce
def reduce[A1 >: Int](op: (A1, A1) => A1): A1
scala> lst.reduce((v1,v2)=>v1+v2)//lst.reduce(_+_)
res54: Int = 44
字符统计
var arrs=Array("this is a demo","good good study","day day up")
arrs.flatMap(_.split(" "))
.groupBy(word=>word)
.toList
.map(tuple=>(tuple._1,tuple._2.size))
.sortBy(_._1)
.foreach(println)
- 元组计算
var arrs=Array("this is a demo","good good study","day day up")
arrs.flatMap(_.split(" "))
.map((_, 1))
.groupBy(_._1)
.toList
.map(t=>(t._1,t._2.map(_._2).sum))
.sortBy(_._2)
.foreach(println)