我眼中的Scala-简洁不简单
由于实验需要在半年前开始接触Scala
,之前也学习/使用过TIOBE榜上Top20中一半左右的编程语言,感觉还是Scala
给我印象最深,最近没怎么做相关的开发感觉都开始慢慢淡忘了,上周在技术分享时我对Scala
作了一些总结,顺便在这里写下。
Scala
注意,本文主要是描述我所了解的Scala
相关的基础语法,和Java
相同得在这里就不再累赘。
我眼中的Scala
Scala是一种基于JVM的编程语言,集成了面向对象和函数式编程的特性,既能处理脚本化得临时任务,又能处理高并发场景下分布式大数据应用。
Java
就是因为有JVM
虚拟机才成就了现在的辉煌,Scala
同样是运行在JVM
,大致可以看做Java的升级版,由于现在大数据大势所趋,各种大数据框架的出现导致了Scala
强势崛起!
编程初学者最先接触的应该就是”Hello,World”,而WordCount可以看做大数据编程的入门必学技能,所以现在我们在”Hello,World”上实现CharCount:1
2
3
4
5
6
7
8
9//Scala Application
val str="Hello,wrold"
val data=str
.split(Array(',',' '))
.flatMap(for(c<-_) yield (c,1)) //好Api
.groupBy(_._1)
.mapValues(_.size)
println(data)
假如你是用Java
程序来写,最方便的莫过于HashMap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//Java Application
String str="Hello,World";
HashMap<Character,Integer> map=new HashMap<Character,Integer>();
for(Character ch:str.toCharArray())
{
if(!",".equals(ch))
{
if(!map.containsKey(ch))
{
map.put(ch, 0);
}
map.put(ch, map.get(ch)+1);
}
}
System.out.println(map);
其结果为
Map(e -> 1, l -> 3, H -> 1, r -> 1, w -> 1, o -> 2, d -> 1)
从上面的程序中看出Scala
的几大特点:
- 链式操作
- 变量不变性
- 丰富的Api
- 简洁不简单(不需要加分号,返回值时不需要return关键字,还有强大的语法糖)
现在已经有非常多优秀的项目都是使用Scala
来编写的:
- Spark:不用多说,感觉最近
Scala
的崛起就是因为该项目的横空出世 - Akka:分布式、高并发、高可伸缩性的消息驱动模型
- Kafka:一个高吞吐量的分布式消息系统
- play:一个高性能的Web框架,据说最近版本用Scala重写了
- 据说Twitter公司好多中间件都是使用Scala来编写的
编程IDE
目前编写Scala
程序有两种比较流行的IDE:Eclipse for Scala、idea community edition,注意,eclipse版感觉功能没idea强,但是速度要比idea快(可能是自己电脑配置差的原因),还有下载idea时要下载社区版,他是免费的。
当然如果是初学者的话还是建议使用万能sublime text来写,自己添加一个插件就好。Tools->Build System->New Build System…,然后输入:
{
"cmd": ["scala","$file"],
"selector": ["source.scala"],
"shell": "true"
}
这样Scala
后缀的程序在Sublime中直接使用Ctrl+B
即可运行
变量定义
标准格式:{val|var} 变量名[:类型]=[new]Class
1 | val a=5 //推断为Int类型 |
函数定义
标准格式:def 方法名(参数列表)[:返回类型]={//方法体}
1 | ////推断返回String |
变量不变性
Scala
编程最为推崇的就是变量不变性:变量一旦定义了之后不再改变
- 符合大数据的思想,比如分布式文件系统上只能进行增和删除操作,并没有修改操作
- 在多线程环境下这个变量是安全的
在Scala
中有两种类型的不变性
val
vsvar
如果用val
定义之后这个变量则不能再被其他变量进行赋值,但是可以对其内容进行修改,val
定义的变量就没有这个限制。1
2
3
4
5
6
7
8
9import scala.collection.mutable.Map //显示得先导入可变的Map包
val a=5
a=6 //则会报错:reassignment to val
//如果a用var定义则无影响
val map=Map(1->2)
map.put(2,3) //这里是修改了变量的内容
map.foreach(println(_)) //会输出(2,3),(1,2)
immutable
vsmutable
如果定义的变量属于immutable
包,则定义之后该变量的内容再不能再被修改(不显式导入默认为该包下的类型),mutable
包下面的类则无此限制。
1 | val immap=Map(1->2)//默认为immutable包下面的类 |
val x=scala.collection.mutable.Type()
可以理解为指针常量,指向的地址不可以重新赋值,但内容可以改变var x=scala.collection.immutable.Type()
可以理解为常量指针,指向的地址可以变,但内容不可以重新赋值
控制结构和函数
加强版的if
if
除了正常的逻辑判断外,还可以直接返回相应的值,并且还支持返回不同类型的值
1 | val a=if(false) 1 else "2" |
该功能在实际编程中灰常有用,之前写Java的时候都不得不在if外先定义变量,再在里面赋值-_-
带有守卫条件的for
守卫是什么东东?你可以理解为for
里面的条件控制
先来看下Scala
的for
循环(与传统的三层for结构大不相同,不过与传统的foreach有点类似)
1 | for(i<-0 until 5) println(i) //可以输出0,1,2,3,4 |
那如果我需要双重循环呢?1
for(i<-0 to 5;j<-i+1 to 5) println(i*j) //大致就是5*5下三角的矩阵相乘值
再来看一下守卫1
for(i<-0 to 5;if i%2==0) println(i) //输出0,2,4
这个守卫表示只有i%2==0
的情况下才会进入循环体,这样是不是很方便。
没有continue、break的while
这个我就不吐槽了,Scala
作者竟然认为continue
、break
这两个这么顺手控制循环的功能是没用的,完全可以在守卫中加入条件判断来控制,这叫我们这帮C系狗情何以堪-_-
数组
数组作为绝大多数编程语言中的一个经典类型,在Scala
是不支持[]
这种定义的,而是用Array
来定义。
标准格式:[val|var] 变量名[:Array[数组类型]]=Array(…)|new Array(..)
1 | val array:Array[Int]=new Array[Int](5) |
类和伴生对象、特质
类
定义:class 类名(构造参数列表) extends,with 继承的类或者接口
与其他的面向对眼语言不同,Scala
中的类支持直接在类名后跟随构造函数列表
1 | class Person(name:String){ |
伴生对象
类没有静态方法或者静态字段,需要用Object
这个语法结构来达到同样的目的,一个Object
一般与对应的类要在同一个文件中(通过Object
就可以实现传说中的不用new
关键词来实例化对象)
1 | class Student(name:String) |
啥都不是说了,没有static
是有点奇怪,但是这个Object
也不错啊,用习惯就好了(^_^)
接口的加强版-特质
当然Scala
里面也是没有Interface
这种东西的,而是用trait
特质来代替,这个特质类似接口,但是他可以自己实现方法(惊呆了,-_-),这样一来Scala
就支持传说中的多继承了,关于多继承里面的菱形问题Scala
是通过最后一个相同名称的方法来解决的。
1 | trait Logger{ |
输出的结果为:
s111111111
remain66.0
s222222222
amount>balance,can't withdraw
withdraw34.0
remain66.0
柯里化
可以将函数的参数列表使用多个括号分类来调用(在其他地方没见过吧~^_^)
1 | def mul(x:Int,y:Int)=x*y //普通青年 |
一般我们在一个函数中如果需要传的参数可以分为几个类别,这样使用柯里化可以让整个函数的调用更加清晰一点。(恩,的确也是)
模式匹配和样例类
模式匹配
模式匹配
match
是一个更好的switch
Scala
的match
除了可以匹配确定值以外,他还有:
- 可以匹配数组、元祖、样例类等
- 可以在匹配时加入守卫(就是匹配的判断条件啦^_^)
- 可以在模式匹配之后进行值的返回
- 不会有意外掉落到下一个分支问题(也就是没有
break
) - 如果得不到匹配会跑出异常,所以如果不确保能全部覆盖匹配则再最后用
_
占位符来匹配所有情况
1 | val ch:Char='-' |
PS:功能很强大吧,在
Scala
的程序里面随处都可以看到match
,甚至很多情况下使用match
来完成if-else
的操作
样例类
样例类是一种特殊的类,它们经过优化以被用于模式匹配
你可以把样例类看做一个结构体,里面定义数据类型,然后在match
操作的时候得以方便得传参
1 | abstract class Amount //定义一个抽象类 |
PS:
Scala
的框架中样例类也用的特别多,比如说Spark
Actor
Actor
提供了并发程序中与传统的基于锁结构不同的另一个选择
他有两种发送消息的方式:
actor!message
:发送消息之后非阻塞的,也没有返回值
1 | import scala.actors.Actor |
这样就会输出
send Hi
send Goodbye
hello!
bye bye!
actor!?message
:发送消息之后会阻塞下面代码的执行,而且还是可以得到返回值的
1 | import scala.actors.Actor |
输出的结果为:
the balance less than amount
false
Desposit 100.0
true
WidthDraw50.0
true
account.getBalance50.0
PS:大名鼎鼎的
AKKA
就是基于Actor
来实现的,是分布式、高并发的消息驱动框架(听听就是高大上~^_^)
隐式转换
你知否曾希望某个类有某个方法,而这个类的作者却没有提供你?
所谓隐式转换函数指的是那种以
implicit
关键字声明的带有单个参数的函数。
1 | //定义一个现金转换类 |
在使用隐式转换时只需要确保调用之前 已经直接了定义的隐式转换方法即可,而且一般定义转换名称为元类型2目标类型
,这样比较好记啊(^_^)
Note:在Scala编写框架时他的功能类往往不会直接暴露出来,而是通过隐式转换的方式来让用调用 (这点可以让你的框架代码结构非常清晰,但是。。。你要看源码实际调用类的时候就很难找到了)
还有还有
- 元组:这不是元祖月饼-_-,接触过
Python
的同学应该都知道吧 List,Map,etc
的集合类- 万能占位符
_
,懒人模式用的,因为有时候懒得想变量 lazy
懒加载,在调用的时候才会加载。。明显有好处嘛^_^- 用索引取值一般用圆括号
(i)
,定义变量类型、泛型一般用方括号[K,V]
- 其他的好也好多都不熟悉了 -_-
参考
- 《快学Scala》
- 《Scala编程-中文版》
- Scala
本作品采用[知识共享署名-非商业性使用-相同方式共享 2.5]中国大陆许可协议进行许可,我的博客欢迎复制共享,但在同时,希望保留我的署名权kubiCode,并且,不得用于商业用途。如您有任何疑问或者授权方面的协商,请给我留言。