Scala学习补充篇

Posted Mr.zhou_Zxy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala学习补充篇相关的知识,希望对你有一定的参考价值。

一 主构造器和辅助构造器

1 构造器介绍

和Java一样,scala构造对象也需要调用构造方法,并且可以右任意多个构造方法(即Scala中构造器也支持重载)
Scala构造器包括主构造器和辅助构造器

2 构造器基本语法

class 类名(形参列表) {  // 主构造器   注意这种定义格式和java不同
   // 类体
   def  this(形参列表) {  // 辅助构造器
   }
   def  this(形参列表) {  //辅助构造器可以有多个...
   }
} 

3 注意事项

1.scala构造器作用是完成对新对象的初始化,构造器没有返回值
2.主构造器的声明直接放置于类名之后
3.主构造器会执行类定义中的多有语句(方法除外),这里可以体会到Scala的函数式编程和面向对象编程融合在一起,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别
4.如果主构造器无参数,小括号可以省略,构建对象时调用的构造方法的小括号也可以省略
5.辅助构造器名称为this(这个this和Java不一样),多个辅助器通过不同的参数列表进行区分,在底层就是构造器重载
6.如果想让主构造器编程私有的,可以在()之前加上private,这样用户只能通过辅助构造器来构造对象
7.辅助构造器的声明不能和主构造器的声明一致,会发生错误
8.辅助构造器的第一行有一定要调用主构造器或者其他辅助构造器

4 案例说明

lass People{
    @BeanProperty var name:String = _
    @BeanProperty var age:Int = _ 
    
    println("主构造器")
    //辅助构造器的第一行必须调用其他的构造器
    def this(name:String,age:Int){
        this()//调用主构造器
        this.name = name
        this.age = age
        println("辅助构造器1")
    }
    def this(name:String){
        this(name,0)//调用辅助构造器
        println("辅助构造器2")
    }
}

二 Final修饰类

object FinalCode1 {
    final class people {
        def A(): Unit = {
            println("zxy")
        }
    }
    class B extends people {
        //这里就是报错
    }
}
object FinalCode2 {
    class people {
        def A(): Unit = {
            println("zxy")
        }
    }
    class B extends people {
        //这里就是编译成功
    }
}

三 apply方法和unapply方法

1 apply方法

当Scala中类或者对象有一个主要的用途的时候,apply方法就是一个很好的语法糖

  • 举例:
class Foo(foo:String){

}
object Foo{
    def apply(foo:String):Foo={
        new Foo(foo)
    }
}

定义一个Foo类,并且在这个类中,有一个伴生对象Foo,里面定义一个apply方法。有了这个apply方法后,我们在调用这个Foo类的时候,用函数的方式来调用:

object Client{
    def main(args:Array[String]):Unit = {
        val foo = Foo("Hello")
    }
}

我们用Foo("Hello)的方法,就得到一个Foo类型的对象,这一切就是apply的功劳,如果没有apply方法,我们将需要使用new关键字来得到Foo对象

2 apply方法做工厂

apply方法最佳的实践方式之一就是用来做工厂,比如在Scala的标准库中,许多集合类给我们提供了apply方法来创建集合:

类型1object num{
    def main(args:Array[String]):Unit = {
        val arr = new Arrray[Int](3)
        arr(0) = 1
        arr(1) = 2
        arr(2) = 3
        arr.foreach(x => print(x + " "))
        println()
    }
}
类型2object num{
    def main(args:Array[String]):Unit = {
        val arr = Array(1,2,3)
        arr.foreach(x => print(x + " "))
        println()
    }
}

类型1和类型2都是用来创建Array。

第一种方式是使用new关键字,这是传统的面向对象的方式。

第二种方式呢?我们可以点Array查看一下底层源码,会发现其底层源码中会有一个apply方法

val array = Array("1","2","3")

后台代码有apply支持:
implicit def canBuildFrom[T](implicit t: ClassTag[T]): CanBuildFrom[Array[_], T, Array[T]] =
   new CanBuildFrom[Array[_], T, Array[T]] {
   def apply(from: Array[_]) = ArrayBuilder.make[T]()(t)
   def apply() = ArrayBuilder.make[T]()(t)
}

3 unapply方法

从上面的例子发现,apply其实是有点类似于Java中的构造方法,接受构造参数变成一个对象。

而unapply是接受一个对象,从对象中提取出相应的值。

unapply主要适用于模式匹配

package day05

object Demo5 {
    def main(args: Array[String]): Unit = {
        testunapply()
    }
    //测试端口实现
     def testunapply() = {
        val money = Money(10.1,"RMB")
        money match {
            case Money(num,"RMB") => println("RMB:" + num)
            case _ => println("Not RMB!")
        }
    }
    //伴生类
    class Money(val value:Double,val country:String){}
    //伴生对象
    object Money{
        //接受构造参数并变成一个对象
        def apply(value: Double, country: String): Money = new Money(value, country)
        //接受一个对象,从中提取出相应的值
        def unapply(money: Money): Option[(Double, String)] = {
            if(money == null){
                None
            }else{
                Some(money.value,money.country)
            }
        }
    }
   
}

四 abstract抽象类

在Scala中可以定义一个抽象类,抽象类可以定义一些方法,但是不需要实现他们。相反,继承抽象类的子类需要实现这些方法,抽象类是不能被实例化
类型1:非抽象对象,定义方法需要实现
class Element{
    def contends:Array[String] = _
}
类型2:抽象方法,定义方法可以不实现,但是其子类必须实现该方法
abstract class Element{
    def contends:Array[String]
}
class A extends Element{
    override def contends: Array[String] = _
}

五 Nothing -> AnyRef -> AnyVal -> Any

1.Any是abstract类,他是Scala类继承结构中最底层的。所有运行环境中的Scala类都是直接或者间接继承来自Any的类,他就是其他语言中的Object
2.Nothing是所有类的子类,Nothing没有对象,但是可以用来定义类型,例如:抛出异常,则不论该方法应该返回哪种类型,异常的返回值是Nothing
3.AnyRef是所有应用类型的基类
4.AnyVal是所有值类的基类

六 正则表达式

符号含义
.匹配任何字符(\\n除外)
^匹配字符串起始部分
$匹配字符串终止部分
*匹配0次或者多次前面出现的正则表达式
+匹配1次或者多次前面出现的正则表达式
匹配0次或者1次前面出现的正则表达式
{N}匹配N次前面出现的正则表达式
{M,N}匹配M~N次前面出现的正则表达式
\\d匹配任何十进制数字
\\w匹配任何字母数字字符
\\s匹配任何空格字符
\\b匹配任何单词边界
\\N匹配已保存的子组
\\A匹配字符串的起始

七 中缀表达式( : : )

中缀表达式得关键字是::,下方代码通过match+::进行模式匹配。

只有当第一个case条件中的各级匹配成功之后,才会执行第一条语句,并执行println语句。

匹配不成功则输出对应的语句

object num1{
    def main(args: Array[String]): Unit = {
        List(1,2,3,4,5,6,7) match {
            case one :: two :: three :: four :: rest => println(one,three,rest)
            case _=>println("中缀表达式")
        }
    }
}
输出结果:
(1,3,List(5, 6, 7))

前四个数字分别与one,two,three,four匹配,剩下的[5,6,7]则分配给rest

object num2{
    def main(args: Array[String]): Unit = {
        List(1,2) match {
            case one :: two :: three :: four :: rest => println(one,three,rest)
            case _=>println("中缀表达式")
        }
    }
}
输出结果:
中缀表达式

数字1与数字2分别与one,two匹配,但是没有全部匹配,在println中也无法输出three,rest

所以匹配失败,输出中缀表达式

八 闭包(Closure)

在Scala中,你可以在任何领域定义函数、包、类,在函数体内可以访问到应用作用域的任何变量

闭包是一个函数,其返回值依赖于声明在函数包部的一个或者多个变量

通常来说,可以将闭包看作是可以访问一个函数里面局部变量的另一个函数

object Demo7_closesure {    
    def main(args: Array[String]): Unit = {        
        def mulby(factor:Double) = (x:Double) => 
        factor * x        
        val triple = mulby(3)        
        val half = mulby(0.5)        
        println(triple(14) + "--->" + half(14))   
    }
}

​ mulby首次调用的时候我们将factor设置为3,该变量会在(x:Double) => factor * x函数中的被引用,也就是说对应的函数变为了:(x:Double) => 3 * x。该函数存储到了triple

​ mulby二次调用的时候我们将factor设置为0.5,该变量会在(x:Double) => factor * x函数中的被引用,也就是说对应的函数变为了:(x:Double) => 0.5 * x。该函数存储到了half

​ 在这里,每个函数都称为闭包(closure)。

  • 典型例题
object num{
    def main(args: Array[String]): Unit = {
        val result = someCurring(4)(3)
        println(result)
    }
    def someCurring(x:Int):(Int) => Int = {
        def s(y:Int) = 2*x
        s
    }
}
输出8
object num{
    def main(args: Array[String]): Unit = {
        val result = someCurring(4)(3)
        println(result)
    }
    def someCurring(x:Int):(Int) => Int = {
        def s(y:Int) = 2*y
        s
    }
}
输出6

九 柯里化(Curring)

指的将两个参数的函数变为一个新的一个参数的函数的过程。

柯里化和闭包性质相同

package com.qf.bigdata.scala.day3

object Demo8_Currying {
    def main(args: Array[String]): Unit = {

        def totalSum(num1:Int, num2:Int) = println(num1 + num2)
        // totalSum(1,2)

        // totalSum(1)(2)
        def total(num1:Int) = (num2:Int) => println(num1 + num2)

        totalSum(1, 2)
        total(1)(2)
    }
}

十 Scala特质和Java的Interface接口

1 特质与interface比较

Scala的特质具有Java的接口和类的部分特性:
1.Scala的特质能够提供具体的方法的实现,而Java接口只有方法的定义,这点很像Java的抽象类
2.Scala同Java,都不能进行多继承,但是前者可以实现多特质,使用with关键字。这点和Java的接口相同
3.Scala的特质在对象生成时临时加入,Java没有这个特性
4.Java只能在类层面添加接口的实现,而Scala可以在类和对象层面上混入特质

2 Java抽象类

1.抽象类不能被实例化,实例化的工作应该交由它的子类来完成,它只需要有一个引用即可
2.抽象方法必须由子类来重写
3.只要包含一个抽象方法的抽象类,该方法必须要定义成抽象类,不管是否还包含其他方法
4.抽象类中可以包含具体的方法,也可以不包含抽象的方法
5.子类中的抽象方法不能与父类中的抽象方法同名
6.abstract不能与final并列修饰同一类
7.abstract不能与private、static、final、native并列修饰同一个方法

3 接口

1.interface的方法的访问权限应该被声明为public
2.接口可以定义“成员变量”,因为接口中的成员变量可以自动变为public static final,可以通过类名点访问
3.接口中不存在实现的方法
4.实现接口的非抽象类必须要实现该接口的所有方法,抽象类可以不用实现
5.不能通过new实例化一个接口,但是可以声明一个接口变量,该变量必须引用一个实现该接口的类的对象
6.在实现多接口的时候一定要避免方法名的重复

4 特质

1.scala中的类只能继承一个超类,可以扩展任意数量的特质
2.特质可以要求实现他们类具备特定的字段、方法和超类
3.与Java 接口不同,Scala特质提供方法和字段的实现
4.当将多个特质叠加使用的时候,顺序很重要
5.若多重继承的基类具有相同的字段或者方法,就会引发菱形继承问题,造成问题的复杂性
6.使用特质可以达到多重继承的类似效果
7.首先调用超类的构造器。 
8.特质构造器在超类构造器之后、类构造器之前执行。所有特质构造完毕,子类被构造。  
9. 每个特质中,父特质先被构造。

十一 set集合

	是一个没有先后次序的集合。如果要有序SortedSet,这种集合元素会以某种顺序访问
  • 代码
object ScalaSetTest{
    def main(args: Array[String]): Unit = {
        // 创建一个可变set
        val set: mutable.HashSet[Int] = new mutable.HashSet[Int]()
        // 往set中加入元素1
        set+=1
       // 添加另一个set中元素
        set.add(2)
		//添加序列
        set ++= Set(1,8,9)
        // 移除一个元素
        set-=2
        // remove等价于-=
        set.remove(2)   
        set.foreach(print)
    }
}

//Set会自动去重
输出结果:
9
1
8

十二 Option

1 Option的介绍

	为了让所有东西都是对象的目标更加一致,也为了遵循函数式编程的习惯,Scala鼓励你在变量和函数返回值的时候可能不会引用任何值的时候使用Option类型。在没有值的时候,使用None,它是Option的一个子类,在有值的时候,就使用some来包含这个值,它也算是Option的一个子类

2 Option作用

	1.Option可以避免判断为空NUll
	2.Option的一个非常惯用的做法是,把Option当成一个集合来看待,集合的大小是1或者0,所以可以把map,filter,foreach等操作在Option上
	3.适用于模式匹配,例如下:nameMaybe中有值的时候,走第一个case。没有值的时候走None的逻辑
val nameMaybe = request getParameter "name"
nameMaybe match {
  case Some(name) =>
    println(name.trim.toUppercase)
  case None =>
    println("No name value")
}

十三 操作符

1 中置操作符

标准:a 标识符 b
举例:
	1 to 10等同于1.to(10)
	1 -> 10等同于1.->(10)

2 一元操作符

	中置操作符是二元的,他有一个显示参数和一个隐式参数,只有一个操作符的参数称为一元操作符。如果它出现在参数之后,他就是后置操作符。
标准: a 标识符
举例:
	1 toString 等同于 1.toString
	<+、-、!、~>这四个操作符可以作为前置操作符,出现在参数之前,他们可以被转换成名为unary_操作符的方法调用
举例:
	+ a 等同于 a.unary_-一样

3 赋值操作符

标准:操作符=以下表达式
例如:
	a += b 等同于 a = a + b
注意:
	1.<= >= != 不是赋值操作符
	2.以=开头的操作符不是赋值操作符
	3.如果a有一个名为操作符=的方法,那么该方法会被直接调用

十四 Type关键字

	Scala里的类型,除了在定义class,trait,object时会产生类型,还可以通过type关键字来声明类型。
	type相当于一个类型别名。
	Type通常用于声明某种复杂类型,或者用于定义一个抽象类型
    类型1object test extends App{
        //将type(S)设置为String类型
        type S = String
        var name:S = "ZXY"
        println(name)
    }
    类型2object ABC {
        class A{
            //定义一个type变量
            type T
            def text(t:T): Unit ={
                println(t)
            }
        }
        class B extends A{
            //继承A类,并重载type,设置为Int类型
            override type T = Int
        }
        class C extends A{
            //继承A类,并重载type,设置为String类型
            override type T = String
        }
        
        def main(args: Array[String]): Unit = {
            //new一个类
            val b: B = new B()
            val c: C = new C()
            b.text(21)
            c.text("zxy")
        }
    }

在这里插入图片描述

以上是关于Scala学习补充篇的主要内容,如果未能解决你的问题,请参考以下文章

Scala学习-”.var“自动补充显示数据类型

scala Actor -03

linux打开终端如何启动scala,如何在终端下运行Scala代码片段?

Scala函数式编程函数式的数据结构 下

[python IO学习篇] 补充.py文件是中文, .ini文件内容是中文

VSCode 配置 用户自定义代码片段 自定义自动代码补充