scala

scala 介绍

scala 基于JVM的语言,静态类型,和java互相操作

使用

;可写可不写

  • 变量 : var
  • 常量 : val

函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//主函数
def main(args: Array[String]) {
println(hello("scala"))
}
//普通函数
def hello(name:String) :String={
"Hello:" + name
}
//匿名函数:
val add=(x:Int,y:Int)=>x+y
//颗粒化:
//作用是将两个参数的函数,转换成两个函数,第一个函数的参数为两个参数函数的第一个参数,同理,第二个函数的参数为第二个参数。
def add2(x:Int)(y:Int) = x+y
//Spring* 可变参数,可以传入a,b,c,d 然后就会打印四个字符,传入几个参数使用几个
def printEveryChar(c:String*)={
c.foreach(x=>println(x))
}

函数-头等公民

Scala 中函数为头等公民,你不仅可以定义一个函数然后调用它,而且你可以写一个未命名的函数字面量,然后可以把它当成一个值传递到其它函数或是赋值给其它变量。

  • (x :Int ) => x +1这是个函数字面量,它的功能为+1. 符好 => 表示这个函数将符号左边的东西(本例为一个整数),转换成符号右边的东西(加 1)。

  • 我们了解到了函数字面量的基本概念,它可以作为参数传递个其它函数,比如很多 Scala 的库允许你使用函数作为参数,比如 foreach 方法,它使用一个函数参数,为集合中每个运算调用传入的函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    scala> val someNumbers = List ( -11, -10, - 5, 0, 5, 10)
    someNumbers: List[Int] = List(-11, -10, -5, 0, 5, 10)
    scala> someNumbers.foreach((x:Int) => println(x))
    -11
    -10
    -5
    0
    5
    10
  • 再比如,Scala 的集合也支持一个 filter 方法用来过滤集合中的元素,filter 的参数也是一个函数,比如:

    1
    2
    scala> someNumbers.filter( x => x >0)
    res1: List[Int] = List(5, 10)

函数–函数字面量的一些简化写法

  • Scala 提供了多种方法来简化函数字面量中多余的部分,比如前面例子中 filter 方法中使用的函数字面量,完整的写法如下:(x :Int ) => x +1,首先可以省略到参数的类型,Scala 可以根据上下文推算出参数的类型,函数定义可以简化为:(x) => x +1这个函数可以进一步去掉参数的括号,这里的括号不起什么作用x => x +1
  • Scala 还可以进一步简化,Scala 允许使用“占位符”下划线”_”来替代一个或多个参数,只要这个参数值函数定义中只出现一次,Scala编译器可以推断出参数。比如:
1
2
3
4
scala> val someNumbers = List ( -11, -10, - 5, 0, 5, 10)
someNumbers: List[Int] = List(-11, -10, -5, 0, 5, 10)
scala> someNumbers.filter(_ >0)
res0: List[Int] = List(5, 10)
  • 前面例子中我们使用“” 来代替单个的参数,实际上你也可以使用“”来代替整个参数列表,比如说,你可以使用 print 来代替 println ().someNumbers.foreach(println _)。

Scala 编译器自动将上面代码解释成:someNumbers.foreach( x => println (x))

  • 因此这里的“” 代表了 Println 的整个参数列表,而不仅仅替代单个参数。当你采用这种方法使用“”,你就创建了一个部分应用的函数(partially applied function)。 在 Scala 中,当你调用函数,传入所需参数,你就把函数“应用”到参数。 比如:一个加法函数。

    1
    2
    3
    scala> def sum = (_:Int) + (_ :Int) + (_ :Int)
    sum: (Int, Int, Int) => Int
    scala> sum (1,2,3) res0: Int = 6
  • 一个部分应用的函数指的是你在调用函数时,不指定函数所需的所有参数,这样你就创建了一个新的函数,这个新的函数就称为原始函数的部分应用函数,比如说我们固定 sum 的第一和第三个参数,定义如下的部分应用函数:

    1
    2
    3
    scala> val b = sum ( 1 , _ :Int, 3)
    b: Int => Int = <function1>
    scala> b(2) res1: Int = 6
  • 变量 b 的类型为一函数,具体类型为 Function1(带一个参数的函数),它是由 sum 应用了第一个和第三个参数,构成的。调用b(2),实际上调用 sum (1,2,3)。

可变参数,命名参数

重复参数

  • Scala 在定义函数时允许指定最后一个参数可以重复(变长参数),从而允许函数调用者使用变长参数列表来调用该函数,Scala 中使用“*”来指明该参数为重复参数。例如:
1
2
3
4
5
6
7
8
9
10
11
scala> def echo (args: String *) =
| for (arg <- args) println(arg)
echo: (args: String*)Unit
scala> echo()
scala> echo ("One")
One
scala> echo ("Hello","World")
Hello
World
  • 在函数内部,变长参数的类型,实际为一数组,比如上例的 String * 类型实际为 Array[String]。 然而,如今你试图直接传入一个数组类型的参数给这个参数,编译器会报错:
1
2
3
4
5
6
7
scala> val arr= Array("What's","up","doc?")
arr: Array[String] = Array(What's, up, doc?)
scala> echo (arr)
<console>:10: error: type mismatch;
found : Array[String] required: String
echo (arr)
^
  • 为了避免这种情况,你可以通过在变量后面添加 _*来解决,这个符号告诉 Scala 编译器在传递参数时逐个传入数组的每个元素,而不是数组整体。
1
2
3
4
scala> echo (arr: _*)
What's
up
doc?

命名参数

  • 通常情况下,调用函数时,参数传入和函数定义时参数列表一一对应。
1
2
3
4
5
scala> def speed(distance: Float, time:Float) :Float = distance/time
speed: (distance: Float, time: Float)Float
scala> speed(100,10)
res0: Float = 10.0
  • 使用命名参数允许你使用任意顺序传入参数,比如下面的调用:
1
2
3
4
scala> speed( time=10,distance=100)
res1: Float = 10.0
scala> speed(distance=100,time=10)
res2: Float = 10.0

条件表达式

1
2
3
val x = 1;
val a = if(x>0) 1 else 0
println(a)

循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//while
var (n,r) =(10,0)
while(n>0){
r=r+n
n=n-1
}
print(r)
//foreach
//for:打印1到10
for(i<-1 to 10){
println(i)
}
//打印1到10的偶数
for(i<-1 to 10 if i%2==0){
println(i)
}

类:一个源文件中可以有多个类,并且都是public级别

1
2
3
4
5
6
class Person{
var name:String=_ //会生成get 和 set 方法
val age=10 // 只会生成get方法
private[this] val gender = "male" //表示这个常量只能在这个类中使用
}

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//主构造器直接跟在类名后边,主构造器的参数,最后会被编译成字段
//主构造器执行的时候,会执行类中的所有语句
//假设参数声明时不带val 和var ,那么相当于private[this]
class Persion01(name:String,age:Int){
println("this is the primary construtor")
}
class Persion02(var name:String,val age:Int){
println("this is the primary construtor")
var gender :String=_
//附属构造器,名称为this
//每一个附属构造器必须首先调用已经存在的子构造器或者附属构造器
def this(name:String,age:Int,gender:String){
this(name,age)
this.gender=gender
}
}

继承

1
2
3
class Student (name:String,age : Int,val major:String) extends Person(name,age){
print("this is the subclass of Person,major is:"+major)
}

抽象类

  • 重写不是抽象类的方法: 在方法前面加一个override
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
object ScalaTest01 {
def main(args:Array[String]){
val student01 = new Student01
student01.speak
println(student01.name + ":" + student01.age)
}
}
abstract class Person01{
def speak
val name : String
var age : Int
}
class Student01 extends Person01{
def speak{
println("speak!!!")
}
val name = "AAA"
var age = 100
}

trait

  • 字段和行为的集合
  • 混入类中
  • 通过with关键字,一个类可以扩展多个特质
  • 可以和jdk1.8中的接口对比学习,trait就是带有具体实现的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
trait Logger{
def log(msg:String){
println("log:"+msg)
}
//如果继承一个trait但是只有一个而且是第一个则还用entends不用with
class Test extends Logger{
def test{
log("xxx")
}
}
//抽象类
abstract class Account{
}
//多继承用with关键字
class Test extends Account with Logger{
}
Object Basic extends App{
val t = new Test
//打印出来的是log:xxx
t.test
}
}
  • trait 可以继承trait则第一个trait则是声明,第二个trait可以实现第一个trait里面的抽象方法

  • 一个类可以有多个with来实现trait

apply

  • scala没有静态的修饰符,但object下的成员都是静态的 ,若有同名的class,这其作为它的伴生类。在object中一般可以为伴生类做一些初始化等操作,如我们常常使用的val array=Array(1,2,3) (ps:其使用了apply方法)
  • apply的用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class ApplyTest{
val name="clow";
def apply() {
println("class ApplyTest--apply()...");
}
}
//object下的成员默认都是静态的
object ApplyTest{
def apply() = {
println("object ApplyTest--apply()...");
new ApplyTest()
}
}
object Basic4 {
def main(args: Array[String]) {
//类名()->调用了对应object下的apply方法
var a1=ApplyTest()
println(a1.name)
//对象名()->调用了对应class的apply方法
a1() //输出:class ApplyTest--apply()...
}
}

package

  • 包package com.xx.data
    • 支持嵌套,下层可以访问上层作用域中的名称
    • 可串联
    • 顶部标记
    • 包对象
    • 包可见性
    • 包在任何地方都可以引入,作用域至该语句所在块的末尾
    • 重命名引入(xx = > yy)
    • 隐藏方法(xx=>_)
    • 自动引入(java.lang. scala. Predef._)

模式匹配match

非常优秀的case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
object Basic5 extends App{
val value = 1
val result = value match {
case 1 => "one"
case 2 => "two"
case 3 => "some other number"
}
val result2 = value match {
case i if i == 1 => "one"
case i if i == 2 => "two"
case _ => "some other number"
}
def t(obj : Any) = obj match {
case x : Int => println("Int")
case s : String => println("String")
case _ => println("unknown type")
}
}

case class

多用在模式匹配中

  • 构造器中的每一个类型都为val,不建议用var
  • 不用new就可以直接产生实例
1
2
3
4
5
6
7
8
9
10
11
case class Book(name : String, author : String)
object Basic5 extends App{
val macTalk = Book("MacTalk","CJQ")
macTalk match {
case Book(name, author) => println("this is a book")
case _ => println("unknown")
}
}

高阶函数

  • 一个简单的demo
1
2
3
4
5
6
7
8
val l = list(1,2,3,4,5)
//把上面的集合中的每个元素都乘以2
val newList = l.map((x:Int) => 2*x)
//简单点
l.map((x) = 2*x)
l.map(x=2*x)
//比较常用的模式,可以把_当成前面的list中的每一个元素的占位符
l.map(2*_)
  • 函数作为参数

集合

  • List:不变的val l = list(1,2,3,4,5),还有可变的
  • Set: val s = Set(1,2,1) 返回的是1 2不能有重复
  • 元组:
1
2
3
4
5
6
7
//可以有多个值
val hostPort = ("localhost",8080)
//取值
hostPort._1
hostPort._2
//特殊的创建形式,里面只有两个值可以这么写
"a" -> "b"
  • Map
1
2
3
Map ("a" -> "b")
Map ("a" -> "b","c" -> "d")
Map ("a" -> "b","c" -> "d","e" -> "f")

集合操作

  • foreach l.foreach(3*_) 没有返回值

  • filter 过滤 l.filter(x=>x%2==0)

  • zip 聚合:例如a和b都是集合: a zip b 则一一对应的聚合

  • partition:列表分割

    1
    2
    3
    l=List(1,2,3,4,5,6)
    l.partition(_%2 == 0)
    //得到两个小的集合:List(1,3,5) List(2,4,6)
  • flatten:扁平化
    List(List("a","b","c"),List("c","d")).flatten 得到结果是List(a,b,c,d)

  • faltMap(flatten + Map)

    1
    2
    3
    4
    val l = List(List(1,2),List(3,4))
    l.flatMap(x=>x.map(_*2))
    //得到结果意思是,先把集合里面的数据乘以2在压平
    List(2,4,6,8)

泛型

  • 泛型类class pair[T,S](val first:T,val second:S)
  • 泛型方法def compute[T](list:List[T])=...

隐式转换

  • 位于源目标类型的伴生对象中的隐式函数
  • 位于当前作用域可以以单个标识符指代的隐式函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Basic6 {
}
class A{
}
class RichA(a : A){
def rich{
println("rich...")
}
}
object Basic6 extends App{
implicit def a2RichA(a : A) = new RichA(a)
val a = new A
a.rich
//隐式参数
def testParam(implicit name : String){
println(name)
}
implicit val name = "implicit!!!"
testParam
testParam("xx")
//隐式类
implicit class Calculator(x : Int){
def add(a : Int) : Int = a + 1
}
//import xx.xx.Calculator
//因为1是隐式类型,所以调用上面的隐式类
println(1.add(1))
}
张冲 wechat
欢迎扫一扫上面的微信关注我,一起交流!
坚持原创技术分享,您的支持将鼓励我继续创,点击打赏!