• Scala语言


    Scala中函数是一等公民,可以不依赖对象直接创建函数,Scala将函数式编程和面向对象编程融合在一起。 Scala中将对象属性和对象方法与类属性和类方法进行了分离,Scala中的对象相关属性和方法通过 class 关键字定义在对象域中,而类相关的属性和方法通过 object 关键字定义在 伴生对象 中(Scala中没有 static 关键字,伴生对象 编译后会生成 原始对象 的class文件(类名.class)和 伴生对象 的class文件(类名$.class) 笔记语法中的”<xxx>“ 代表用户必填项,”[xxx]“ 代表选填项

    一、基本语法

    声明变量:var|val <变量名> [: 变量类型] = [变量值]

    Scala中分号可不写 val代表不可变量,编译后加上 final 修饰

     

    特殊类型说明
    Unit 等同于 void
    Null 引用类型,但只有一个null实例
    Nothing 在Scala继承链最底层,是任何类的子类,利用 多态 在我们确定一个函数不会正常返回的时候,可以将返回值(异常)返回给其他变量(因为是子类)

    Scala中值类型转换

    • 向下转型直接赋值

    • 向上转型在值上调用 toXxx(如:toDouble、toInt)强制转换函数

    Scala中不支持三目运算符(即:<布尔表达式>?<语句1>:<语句2>),全部使用 if-else 方式实现 Scala中也没有 switch 语句,而是使用 match-case 匹配模式实现

    Scala中for循环控制

    // 打印1到3,闭区间
    for(i<- 1 to 3){
       print(i);
    }
    // 打印1到3,前闭后开
    for(i<- 1 unit 3){
       print(i)
    }
    // 直接遍历集合
    val list = List("h",1,2.2);
    for(item<-list){
       println(item)
    }
    // 循环守卫(也称条件判断式),判断为true进入循环,false则跳过循环
    for(i<- 1 to 3 if i!=2){
       println(i);
    }
    // 循环返回值,对循环中处理的结果返回到一个新的Vector集合中
    var res = for(i<- 1 to 3 )yield i
    // 控制步长,打印1到10 步长为2
    for(i <- Range(1,10,2)){
       println(i)
    }

     

    二、函数式编程基础

    在Scala中函数也被当做一种数据类型,可以作为方法的参数传递,Scala中将没有返回值的函数/方法也称为 过程

    在类中定义方法:

    def <函数名> ([数名称:参数类型]....)[:返回值类型[=]]{ [语句...] [return 返回值] }

    直接定义函数:

    val func ([数名称:参数类型]....)[:返回值类型] =>{ [语句...] [return 返回值] }

    将对象的方法转换为独立的函数:

    val func = obj.func _;

    1. 形参为空可以省略()

    2. def <函数名> ([数名称:参数类型]....)={...} 表示返回值类型不确定(最后一条语句不能带 return 关键字),运行时类型推导完成,语句可以返回 多种类型

    3. def <函数名> ([数名称:参数类型]....):<返回值类型>{...} 表示返回值类型在编译期就确定,语句中只能返回 一种类型

    4. def <函数名> ([数名称:参数类型]....){...} 表示没有返回值(即等价于返回值Unit),即使语句中有return也不生效,这种函数也叫 过程

    5. 如果没有return语句,默认语句的最后一行结果作为返回值

    6. 如果写了语句最后写了return关键字,则返回值类型就不能省略,函数返回不能自行推导

    7. 形参列表和返回值的数据类型可以是值类型和引用类型

    8. 在Scala中声明函数/方法形参列表时,可以指定默认值

    可变参数写法

    def <函数名> (参数名:参数类型*){[语句...]}

    惰性函数

    在将函数返回值赋值给其他变量时候,将这个变量声明为lazy,则该函数不会立即执行,只会在取值的时候才会计算,这种函数叫 惰性函数

    lazy可以修饰val变量,但不能修饰var变量。指定了该变量后也会在用到的时候才会赋值

    三、类与对象

    3.1 基础

    Scala没有public关键字,定义类的时候 属性 默认为private修饰并且默认编译后会提供 属性名() 的方法用来获取该方法,和 属性名_$eq()方法用来设置该属性,如果定义属性时添加上private则编译后不会再添加这两个方法,需要开发者自己提供。定义类的时候 方法 默认为public修饰,protected修饰的属性或者方法只能对子类访问,而子包不能访问 Scala还通过object关键字来定义 伴生对象 ,将所有类属性和类方法全部定义到 伴生对象

    <val|var 对象名> [:类型] = new <类型>();

    3.2 构造器

    Scala包含 主构造器辅助构造器,在scala中体现了函数编程和面向对象编程的思想结合,定义主构造器放在类名之后,且 主构造器 会执行直接放在类中的所有语句。 伴生对象可以通过def apply():<类名>={}方式定义工厂方法,通过 伴生对象 创建对象时不用new直接使用类名(实参列表)形式

    1.想让主构造器变私有,则在()前加上 private 2.主构造器 无参数可以省略类名后面的() 3.构造器参数未使用任何访问符,则形参是局部变量 4.构造器参数使用var定义会则编译后会将变量作为private属性并提供 xxx()方法和xxx_$eq()方法 5.构造器参数使用val定义则编译后变量用private final修饰并提供 xxx()方法 6.创建对象时必须为属性指定初始值,也可以用 - 赋值初始值 7.只有 主构造器 可以调用 父类构造器 ,但是不能通过super调用,可以通过extends 父类(参数)的方式给符类构造器传递参数 8*.在类上加上@BeanProperty注解,会自动生成setter和getter方法

    3.3 包

    3.3.1 包定义

    Scala中的包可以嵌套使用,子包可以 直接访问 父包内容,而父包要通过import来访问子包内容,并且引入包的时候,只能访问该包下面的类,而不能递归访问子包下面的类(即import不是精确引入,不会递归引入) 包中可以包含类对象特质(trait),但是不能包含变量和方法,这是虚拟机局限,因此Scala通过包对象来解决.(每一个包只能有一个包对象)包中对象可以直接调用包对象定义的属性和方法

    定义语法:package object <包名>{[语句...]}

    定义方法和属性时候privateprotected修饰的成员后面可以通过 [包名]来扩大可见性

    3.3.2 包引入

    Scala中的包可以在任何需要的地方引入,从而降低import包的作用范围。

    // Scala中可以在引包的时候将类定义别名
    import java.utils.{HashMap=>JavaHashMap}
    // Scala在引入包的时候可以制定忽略某些类
    import java.utils.{HashMap=_,_}

    3.4 继承

    Scala只支持单继承使用extends关键字,子类继承了父类所有的属性和方法,只是私有属性和方法不能在子类访问 Scala中不存在Java继承时属性存在的动态绑定问题,因为Scala继承属性的时会把属性的属性名()属性名_$eq()一起继承过去,相当于 重写方法 Scala中引用类型强制类型转换通过asInstanceOf[父类]来转换,通过isInstanceOf来判断类型 Scala使用trait关键字声明特质(等同java中的接口),当一个类继了特质,就可以用该特质来引用该类的实例对象

    声明时使用特质:class <子类> extends <父类|特质1> with <特质2> with <特质3> with<特质4>

    创建对象时使用特质(动态混入):<var|val> 对象名 = new 类名 with <特质1> with <特质2> 创建对象时按照定义顺序从左到右加载(初始化特质),而调用覆盖的特质方法时,会从右向左调用,程序运行过程中 不管有无继承关系特质2中 super 指的是特质1 ,如果想让特质2指向自己的 真实父类 ,可以通过 泛型 即:super[特质直接父类].xxx(...)的方式调用

    既有抽象方法又有具体方法的特质也叫 富接口 混入了特质的类,特质中的属性直接加入该类作为类的属性(对象属性或者类属性具体看特质如何定义),并且特质中抽象的字段(未赋值)在具体的类中必须初始化

    特质 可以继承具体的类(相当于Java中的接口可以继承具体的类),然后可以扩展类的功能,混入该特质的类只能继承该特质的超类

    特质 继承了具体的类,如果想限制实现该特质的类 必须是特质超类的子类 才能调用该方法,则可以在方法前加上this.<特质超类名>=>的限定

    3.5 内部类

    // 创建内部类
    new Outter().Inner()
    // 创建静态内部类,在伴生对象中定义
    new Outter.Inner()

    通过 别名=>class 内部类的形式将 外部类.this 替换为前面的 别名 ,这种方式在定义 别名 的时候要将外部类属性 放在别名定义之后,否则编译错误。 非静态内部类 默认与 外部类对象 关联,如果方法需要调用自身 非静态内部类 对象,即屏蔽外对象对内部类对象影响可以通过类型投影,具体做法就是在 非静态内部类 调用自身的方法的形参前面加上 外部类# 前缀,表示忽略内部类对象之间关系。

    3.6 隐式

    1. 隐式转换函数式通过implicit声明 有且只有一个参数 的函数,这种函数在它作用域范围内 自动调用,因为是 自动调用 它的名称并不重要。只是与 函数签名 有关(函数的 参数列表 以及 返回值

    implicit def <函数名>(<需要转换的参数类型> <参数名>):<转换后的参数类型>{[语句...]}

    2. 通过implicit声明的变量叫 隐式值,如果函数要使用 隐式值 需要在 形参 前面也加上 implicit,这样调用该 拥有隐式值形参的函数 在调用时如果没有传入参数,则会在作用范围内寻找通过implicit声明的 隐式值 作为实参。隐式值 的优先级要大于定义形参时指定的 形参默认值

    3. 隐式类通过`implicit`声明在类、伴生对象、包对象中(即:隐式类不能作为顶级类),**主构造器** 只能包含一个参数作为在该范围内被识别并转换的类型(形参不需要`implicit`修饰),在 **隐式类** 作用范围内通过 **隐式类主构造器参数类型的对象** 调用 **隐式类中的方法**(在该类型对象中没定义该方法),则在编译过后会通过 **将该类型对象作为参数传入隐式类,然后通过隐式类调用隐式类中方法** 的方式调用 **隐式类中的方法**,来在隐式类作用范围内增强指定对象

    四、集合基本使用

    在Scala中集合分为两种:变长集合定长集合,并且有时候会用到Java中的集合,则会通过 隐式转换 在两者之间互相转换。

    4.1 Array

    // 直接创建定长数组(泛型是可选的,但必须指定集合长度),不能直接print全部元素
    val array = new Array[Int](3);
    // 通过集合伴生对象的apply方法创建定长数组,可以直接print全部元素
    val array = List(1,2,3);
    // 定义多维数组(只能是定长)
    val arrayDim = Array.ofDim[Int](3,4) //3行4列,元素Int类型
    // 定长集合是定义在scala包中的不需要引入,而变长集合需要单独引用
    import scala.collection.mutable.ArrayBuffer
    // 定长集合转换为变长集合
    val arrayBuffer = array.toBuffer;
    // 创建变长数组
    val arrayBuffer = new ArrayBuffer[Int](3);
    // 向变长数组添加元素
    arrayBuffer.append(1);
    // 变长集合转换为定长集合
    val array = arrayBuffer.toArray;
    // Scala数组转换为Java数组(因为Java数组是变长,所以只能中Scala中的ArrayBuffer转换)
    // 导入隐式转换函数
    import scala.collection.JavaConversions.bufferAsJavaList
    val scalaArr = new ArrBuffer[Int](3);
    // 该方法会需要Java的ArrayList,调用时会被引入的隐式转换函数识别转换为ArrayList
    val javaArr = new ProcessBuilder(scala);
    // Java数组转换为Scala数组
    import scala.collection.JavaConversions.asScalaBuffer
    val scalaArr:mutable.Buffer[Int] = javaArr;

    4.2 Tuple

    元组是一个容器可以存放相同或者不同的元素作为一个整体,Scala中元组最大可以存储22个元素,Tuple1~Tuple22不同的元素个数是不同的元组类型

    // 元组定义
    val tuple = (1,2,"hello",4);
    // 元组访问
    print(tuple._2);// 访问元组第二个元素

    4.3 List

    默认情况下Scala中的List是不可变(如果用可变则需要ListBuffer),并且是一个对象可以直接存储数据。

    //--------------------不可变List
    // 创建list
    val list = List(1,2,3);
    // 创建空集合
    val list = Nil;
    // 访问list元素
    print(list(1));//访问第二个元素
    // 在list之后追加数据
    val list2 = list:+4;//list并没有改变,而是返回一个添加元素后的新集合
    // 在list之前添加数据
    val list = 4+:list
    // 将每一个元素加到集合中
    val list = 1::2::3::List(1,2,3)::Nil;
    // 将集合分解,并把集合中的每个元素添加到指定末尾集合中
    val list = 1::2::3::List(1,2,3):::Nil;
    //----------------------可变ListBuffer
    import scala.collection.mutable.ListBuffer
    // 创建可变list
    val listBuffer = ListBuffer(1,2,3);
    // 从可变list删除元素
    listBuffer-=1;
    // 向可变list添加元素
    listBuffer+=1;
    // 合并另外一个集合(可以是不变list)
    listBuffer++list;

    4.4 Queue

    在Scala中有scala.collection.mutable.Queuescala.collection.immutable.Queue两种队列,一般开发中用到的是可变队列

    // 创建可变队列
    import scala.collection.mutable.Queue
    val queue = Queue(1,2,3);
    // 向队尾增加元素
    queue+=4;
    // 向对位添加集合中的所有元素
    queue++=List(1,2,3);
    // 从队列头部取出元素
    queue.dequeue();
    // 向队列尾部添加元素
    queue.enqueue(4,5,6);
    // 返回队首元素(对队列无影响)
    queue.head;
    // 返回队尾元素(对队列无影响)
    queue.last;
    // 返回除第一个意外的所有元素
    queue.tail;

    4.5 Map

    在Scala中有scala.collection.mutable.Mapscala.collection.immutable.Map两种Map,默认是不可变Map,其中不可变Map有序,可变map无序。在Scala底层Map元素是Tuple2类型

    // 创建Map
    val map = Map("a"->10,"b"->20);
    // 以二元组形式创建Map
    val map = Map(("a",10),("b",20));
    // 使用map取值,如果key不存在则抛出异常
    map.map("a");
    // 检查元素是否存在
    map.contains("a");
    // get取值,如果存在返回some,不存在返回none
    map.get("a").get
    // getOrElse取值,如果不存在返回默认值(第二个参数指定的值)
    map.getOrEles("a","默认值");
    // 添加元素
    map+=("c"->30,"d"->40);
    // 删除元素
    map-=("c"->30,"d"->40);
    // 取出的元素是Tuple2类型,因此遍历可以通过元组的方式访问
    for(ele<-map){ele._1;ele._2}

    4.6 Set

    在Scala中有scala.collection.mutable.Setscala.collection.immutable.Set两种Map,默认是不可变Map

    // 创建可变set
    import scala.collection.mutable.Set
    val set = Set(1,2,3);
    // 向set中添加元素
    set+=4;
    // 在set中删除元素
    set-=4;

    ###

    五、集合高级使用

    5.1 映射操作

    将集合中的每一个元素通过 映射函数 转换为新结果集合

    val list = List(1,2,3);
    list.map(x=>x+1)

    5.2 扁平化

    val list = List(1,2,3);
    list.flatMap(_.toString);

    5.3 元素过滤

    val list = List(1,2,3);
    list.filter(_>2);

    5.4 化简

    val list = List(1,2,3);
    // 左边第一个元素开始迭代向后执行函数
    list.reduce(_+_);//等同于reduceLeft

    5.5 折叠

    val list = List(1,2,3);
    list.fold(4)(_+_);//等价于在集合左侧添加元素后执行第二个函数参数
    4/:list(_+_)

    5.6 扫描

    // 对集合的每个元素进行fold操作,让后将所有中间结果保存到一个集合中
    val list = List(1,2,3);
    val list2 = list.scan(4)(_+_);

    5.7 拉链合并

    val list1 = List(1,2,3);
    val list2 = List(4,5,6);
    list1.zip(list2);//配对形成元组集合(1,4),(2,5),(3,6)

    5.8 迭代器

    迭代器遍历元素只能访问一次,第二次访问需要重新获取迭代器

    val list = List(1,2,3);
    val iterator = list.iterator;
    while(iterator.hasNext){
       iterator.next()
    }
    // 重新获取迭代器,以临另一种方式迭代
    for(ele<-iterator){
       ele;
    }

    5.9 流

    steam是一个存放无穷元素的集合,主要用来通过指定规则动态生成元素。末尾元素遵循lazy规则

    def numsForm(n:BigInt):Stream[BigIni]=n#::numsForm(n+1)
    numsFrom(4).tail

    5.10 视图

    使用视图对集合的所有操作并不会立即执行,而是在用到集合元素的时候才去执行

    val list = List(1,2,3)
    list.view.filter(_>1);

    5.11 并行集合

    val list = List(1,2,3);
    list.par.map(_+2);

    5.12 操作符扩展

    class oper{
       var money:Int = _;
       // 对中置操作符重载
       def +(n:Int):Unit={
           this.money+=n;
      }
       // 对后置操作符重载
       def ++():Unit={
           this.money+=1
      }
       // 对前置操作符重载,方法前需要加上unary_前缀
       def unary_!():Unit={
           this.money=-money;
      }
    }

    六、模式匹配

    6.1 基本介绍

    val oper = '+';
    oper match{
       case '+'=>print("加法");
       // 如果都没匹配,最终会执行 case_后面的语句
       case _=>
    }

    6.2 守卫

    val num =120
    num match{
       //此时的_不表示默认匹配
       case _if(mun>100)=>print(num)
       case _=>
    }

    6.3 模式中的变量

    val a = "aaaa"
    a match{
       // 将a中的值传递给mystring
       case mystring => print(mystring);
       case _ =>
    }
    val b = ‘b’
    b match{
       // match中可以有返回值,默认case的最后一条语句为返回值
       case ‘b’ => "hello";
       case _ =>
    }

    6.4 匹配集合

    Array(0) // 匹配只有一个元素且为0的数组
    Array(x,y) // 匹配数组有两个元素并将两个元素赋值x,y
    Array(0,_*) // 匹配数组以0开始

    0::Nil // 匹配只有一个元素且为0的List
    x::y::Nil // 匹配有两个元素并将两个元素赋值为x,y
    0::tail // 匹配第一个元素为0的List

    (0,_) // 匹配第一个元素为0的tuple2
    (y,0) // 匹配第二个元素为0的tuple2,并把第一个元素赋值为x

    6.5 对象匹配

    // 伴生对向的unapply方法返回Some集合为匹配成功,返回None集合为返回失败
    Object Square{
       def unapply(d:Double):Option[Double]={
           Some(math.qurt(d))
      }
    }

    val num = 10.1
    num match{
       // 调用Square的unapply方法隐式传入num,返回结果Some的值赋给n
       case Square(n) => print(n);
       case _ =>
    }

    6.6 样例模式

    // 声明样例类后面必须带一个参数,该参数默认用val修饰作为不可变属性
    case class A(v:Int){}

    6.7 密封类

    通过sealed关键字声明的类的所有子类必须在相同源文件中定义


    七、函数式高级编程

    7.1 偏函数

    // 匿名内部类形式定义偏函数,表示接受的参数是Any,返回值类型是Int,collect支持偏函数,map不支持偏函数
    val paritalFum = new ParitalFunction[Any,Int]{
       // 如果返回true 会调用 apply方法创建对象,如果为false则过滤掉元素
       override def isDefinedAt(x:Any)={
           
      }
       // 返回Int类型对象
       override def apply(v:Any)={
           
      }
    }

    // 简化形式,返回类型必须统一,如果不统一返回类型是Any
    val list = List(1,2,3).collcet{
       case i:Int=>i+1;
       case j:Double=>(j+1).toInt;
       case _ =>
    }

    7.2 匿名函数

    val fun = (x:Int)=>{
       x+1
    }

    7.3 高阶函数

    能够 接收函数作为参数的函数 或者 能够返回函数的的函数 叫做 高阶函数

    // 接受函数作为参数
    // 接受Double类型的参数返回Int类型参数
    def fun(f:Double=>Int,n:Double){
       f(n)
    }
    // 返回函数作为参数,也称为闭包
    def minusxy(x:Int){
      (y:Int)=>x+y;
    }
    // 或者直接使用匿名函数返回高阶函数,也称为闭包
    val fun = (x:Int)=>{
      (y:Int)=>x+y
    }
    // 闭包就是一个函数与相关引用环境组合成的一个整体

    7.4 参数类型推断

    1. 参数类型可以推断时,可以省略参数类型

    2. 传递函数时,只有单个参数可以省略括号

    3. 如果参数的变量(无论=>左侧有多少变量),在=>右侧只出现一次,则可以省略=>以及左侧内容,右侧内容的所有变量用_表示

    7.5 函数柯里化

    接收多个参数的函数 转化为接受 单个参数的的函数 的过程就叫 柯里化

    // 通过柯里化比较字符串大小并且转换成小写
    implicit class TestEq(s:String){
       def checkEq(str:String)(f:(String,String)=>Boolean):Boolean={
           f(s.toLowerCase,str.toLowerCase);
      }
    }
    // 调用
    val str = "Hello";
    str.checkEq(str)(_.equals(_));

    7.6 抽象控制

    抽象控制是一类特殊的函数,它的 参数是函数 ,函数没有输入值也没有返回值

    def abstractCtrl(f:=>Unit){
       f
    }
    // 直接将语句块传入到f函数中
    abstractCtrl{
       // 语句块
    }

     


  • 相关阅读:
    使用VisualVM进行性能分析及调优(转)
    Eclipse插件开发中对于Jar包和类文件引用的处理(彻底解决插件开发中的NoClassDefFoundError问题)(转)
    Adapter 代码中启动报错
    Myeclipse安装SVN插件(转)
    用java程序调用批处理文件
    内存泄露及常见的解决办法(转)
    Adapter 启动时报错——2
    开启Win7系统管理员Administrator账户
    C盘更改文件夹权限
    Adapter 启动时报错
  • 原文地址:https://www.cnblogs.com/leon618/p/13783385.html
Copyright © 2020-2023  润新知