当前位置:   article > 正文

Scala语法教程_scala的语法

scala的语法

第一章 Scala入门

1.1概述

Scala是一门以Java虚拟机(JVM)为运行环境并将面向对象和函数式编程的最佳特性结合在一起的 静态类型编程语言(静态语言需要提前编译的如:Java、c、c++等,动态语言如:js)。

1) Scala是一门多范式的编程语言,Scala支持面向对象和函数式编程。(多范式,就是多种编程方

法的意思。有面向过程、面向对象、泛型、函数式四种程序设计方法。)

2) Scala源代码(.scala)会被编译成Java字节码(.class),然后运行于JVM之上,并可以调用现有 的Java类库,实现两种语言的无缝对接。

3)Scala单作为一门语言来看,非常的简洁高效。

4)Scala在设计时,马丁·奥德斯基是参考了Java的设计思想,可以说Scala是源于Java,同时马丁·奥 德斯基也加入了自己的思想,将函数式编程语言的特点融合到JAVA中, 因此,对于学习过Java的同学, 只要在学习Scala的过程中,搞清楚Scala和Java相同点和不同点,就可以快速的掌握Scala这门语言。

1.2环境搭建

基于JDK1.8

下载scala2.12.11

配置scala环境变量

img

1.3代码编写(编译原理)

package chapter01

class Student(name:String,info: Int) {
  def printInfo():Unit ={
    println(name+" "+info+" "+Student.school)
  }
}

object Student{
  val school:String = "河北农业大学"
//伴生对象
  def main(args: Array[String]): Unit = {
    val student = new Student("张三",20)
    student.printInfo()
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

编译后生成的class
Student$.class

package chapter01;

public final class Student$
{
  public static  MODULE$;
  private final String school;

  static
  {
    new ();
  }

  public String school()
  {
    return this.school;
  }
  public void main(String[] args) {
    Student student = new Student("张三", 20);
    student.printInfo();
  }
  private Student$() {
    MODULE$ = this;

    this.school = "河北农业大学";
  }
}
  • 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

Student.java

package chapter01;

import scala.Predef.;
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001}2Aa\003\007\001\037!Aa\003\001B\001B\003%q\003\003\005#\001\t\005\t\025!\003$\021\0251\003\001\"\001(\021\025a\003\001\"\001.\017\025\tD\002#\0013\r\025YA\002#\0014\021\0251c\001\"\0015\021\035)dA1A\005\002YBaa\016\004!\002\0239\002\"\002\035\007\t\003I$aB*uk\022,g\016\036\006\002\033\005I1\r[1qi\026\024\b'M\002\001'\t\001\001\003\005\002\022)5\t!CC\001\024\003\025\0318-\0317b\023\t)\"C\001\004B]f\024VMZ\001\005]\006lW\r\005\002\031?9\021\021$\b\t\0035Ii\021a\007\006\00399\ta\001\020:p_Rt\024B\001\020\023\003\031\001&/\0323fM&\021\001%\t\002\007'R\024\030N\\4\013\005y\021\022\001B5oM>\004\"!\005\023\n\005\025\022\"aA%oi\0061A(\0338jiz\"2\001\013\026,!\tI\003!D\001\r\021\02512\0011\001\030\021\025\0213\0011\001$\003%\001(/\0338u\023:4w\016F\001/!\t\tr&\003\0021%\t!QK\\5u\003\035\031F/\0363f]R\004\"!\013\004\024\005\031\001B#\001\032\002\rM\034\007n\\8m+\0059\022aB:dQ>|G\016I\001\005[\006Lg\016\006\002/u!)1H\003a\001y\005!\021M]4t!\r\tRhF\005\003}I\021Q!\021:sCf\004")
public class Student
{
  private final String name;
  private final int info;

  public static void main(String[] paramArrayOfString)
  {
    Student..MODULE$.main(paramArrayOfString);
  }

  public static String school()
  {
    return Student..MODULE$.school();
  }

  public void printInfo()
  {
    Predef..MODULE$.println(2 + this.name + " " + this.info + " " + Student..MODULE$.school());
  }

  public Student(String name, int info)
  {
  }
}
  • 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

第二章 变量和数据类型

2.1 变量和常量

var 变量名 :变量类型 = 值

val 常量名 :常量类型 = 值

变量类型都可省略,编译器会自动推断

能使用常量的地方不用变量

(1) 声明变量时,类型可以省略

(2) 类型确定后不可以修改

(3) 变量声明时,必须有初始值

(4) 声明变量时,可以使用var和val,var可以变,val不可变

2.2 标识符的规范

以操作符开头,且只包含操作符(+ - * / # !等)

用反引号....包括的任意字符串,即使是 Scala 关键字(39 个)也可以

• package, import, class, object, trait, extends, with, type, for • private, protected, abstract, sealed, final, implicit, lazy, override

• try, catch, finally, throw

• if, else, match, case, do, while, for, return, yield

• def, val, var

• this, super • new • true, false, null

2.3 字符串

(1)字符串,通过+号连接 ,可以字符串乘法

(2)printf 用法:字符串,通过%传值。

(3)字符串模板(插值字符串):通过$获取变量值

var age="hello"
println(s"${age}")

var num = 2.3456
println(f"The num is ${num}%.2f")//保留两位小数
println(raw"The num is ${num}%.2f")//raw 输出原格式

//三引号格式 可以换行写多行文本,保持原格式
s"""""".stripMargin 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.4 键盘输入

StdIn 就是new Scanner

基本语法

StdIn.readLine()、StdIn.readShort()、StdIn.readDouble()

2.5 文件操作

 def main(args: Array[String]): Unit = {
    //读取文件
    Source.fromFile("src/main/resources/test.txt").foreach(print)
    //将数据写入文件 scala没有封装好的写入文件类库 使用Java io即可
    val writer = new PrintWriter(new File("src/main/resources/test1.txt"))
    writer.write("hello scala from java writer")
    writer.close()
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.6 数据类型

1)Scala中一切数据都是对象,都是Any的子类。

2)Scala中数据类型分为两大类:数值类型(AnyVal)、 引用类型(AnyRef),不管是值类型还是引用类型都是对象。

3)Scala数据类型仍然遵守,低精度的值类型向高精度值类型,自动转换(隐式转换)

4)Scala中的StringOps是对Java中的String增强

5) Unit:对应Java中的void,用于方法返回值的位置,表 示方法没有返回值。Unit是一个数据类型,只有一个对象 就是()。Void不是数据类型,只是一个关键字

6) Null是一个类型,只有一个对象就是null。它是所有引用类型(AnyRef)的子类。

Nothing,是所有数据类型的子类,主要用在一个函数没有明确返回值时使 用,因为这样我们可以把抛出的返回值,返回给任何的变量或者函数

img

img

第三章 流程控制

3.1 if else

语法与java没有区别

但每个分支都可以返回值

返回的类型为分支代码块的最后一行内容

val age = StdIn.readInt()

    var result = if (age>18){
      println("18")
      15
    } else{
      "000"
    }
    println(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3.2 for

to unitl

区别就在于until没有右边界

for(i <- 1 to 3){
 print(i + " ")
}
println()
------------------------------------------------------
for(i <- 1.to(3){
 print(i + " ")
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

底层是调用的方法

for (i <- Range(1,10)){}
//不包含边界 Range为伴生对象


for (i <- 1 until 10){}
//底层就是上面
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

scala中的foreach循环

for (i <- Array(10,20,30))
for (i <- List(10,20,30))
for (i <- Set(10,20,30))

  • 1
  • 2
  • 3
  • 4

循环守卫

scala中没有continue

for (i <- 0 to 10 if i!=5){}
  • 1

循环步长

for(i <- 1 to 3 by 2){}
//步长不能为0
  • 1
  • 2

反转 reverse

for(i <- 1 to 3 reverse){}
for(i <- 3 to 1 by -1){}
  • 1
  • 2

循环嵌套

for(i <- 1 to 3; j <- 1 to 3) {
 println(" i =" + i + " j = " + j)
}
//等价

for (i <- 1 to 3) {
 for (j <- 1 to 3) {
 println("i =" + i + " j=" + j)
 }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

循环返回值

很少用

object TestFor {
 def main(args: Array[String]): Unit = {
 var res = for(i <-1 to 10) yield {
 i * 2
 }
 println(res)
 }
}
输出结果:
Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.3 while do

因为 while 中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免 的使用变量,而变量需要声明在 while 循环的外部,那么就等同于循环的内部对外部的变量 造成了影响,所以不推荐使用,而是推荐使用 for 循环。

与java语法相同

3.4 循环中断

Scala 内置控制结构特地去掉了 break 和 continue,是为了更好的适应函数式编程,推 荐使用函数式的风格解决break和continue的功能,而不是一个关键字。Scala中使用breakable 控制结构来实现 break 和 continue 功能。

import scala.util.control.Breaks
import scala.util.control.Breaks._
def main(args: Array[String]): Unit = {
 Breaks.breakable(
 for (elem <- 1 to 10) {
 println(elem)
 if (elem == 5) Breaks.break()//break()
 }
 )
 println("正常结束循环")
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

第四章 函数式编程

img

4.1 函数和方法的区别

1)核心概念

(1)为完成某一功能的程序语句的集合,称为函数。

(2)类中的函数称之方法。

2)案例实操

(1)Scala 语言可以在任何的语法结构中声明任何的语法

(2)函数没有重载和重写的概念;方法可以进行重载和重写

(3)Scala 中函数可以嵌套定义

4.2 函数参数

(1)可变参数

(2)如果参数列表中存在多个参数,那么可变参数一般放置在最后

(3)参数默认值,一般将有默认值的参数放置在参数列表的后面

(4)带名参数

可变类似于python

def hello (*str):
    print(type(str))
hello("sac","1")
------------------------
<class 'tuple'>
  • 1
  • 2
  • 3
  • 4
  • 5
def hello(string: String*)={
      println(string)
    }

println(hello("1","2"))
----------------------------
WrappedArray(1, 2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

默认值

def hello(string: String = "hello")={
      println(string)
    }
  • 1
  • 2
  • 3

带名参数

def hello(name,age)={
      println(name+age)
}
hello(age=15,name="张三")
  • 1
  • 2
  • 3
  • 4

4.3 函数至简原则(重点)

函数至简原则:能省则省

(1)return 可以省略,Scala 会使用函数体的最后一行代码作为返回值

(2)如果函数体只有一行代码,可以省略花括号

(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)

(4)如果有 return,则不能省略返回值类型,必须指定

(5)如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用

(6)Scala 如果期望是无返回值类型,可以省略等号

(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加

(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略

(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略

匿名函数 lambda表达式

4.4 匿名函数

没有名字的函数就是匿名函数。

(x:Int)=>{函数体}

def fun1(name:String): Unit ={
      println(name)
    }
    val tmp = (name:String)=>{println(name)}
    def fun2(fun:String => Unit): Unit ={
        fun("张三")
    }
    fun2(tmp)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

(1)参数的类型可以省略,会根据形参进行自动的推导

fun2((name)=>{println(name)})
  • 1

(2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参 数超过 1 的永远不能省略圆括号。

fun2( name =>{println(name)})
  • 1

(3)匿名函数如果只有一行,则大括号也可以省略

fun2( name => println(name))
  • 1

(4)如果参数只出现一次,则参数省略且后面参数可以用_代替

x fun2( name => println(_))
fun2( println(_))
  • 1
  • 2

离谱版

fun2(println)
  • 1

4.5 函数高级

函数对象

def fun1(name:String): Unit ={
    println(name)
}
val tmp = (name:String)=>{println(name)}
def fun2(fun:String => Unit): Unit ={
    fun("张三")
}
val f1 = fun2 _
val f2: String => Unit= fun1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

val f1 = fun2 _

也可以返回一个函数对象

函数赋值

def func1(s:String): Char=>(Int=>Boolean)={
    def func2(c:Char): Int=>Boolean ={
      def fun3(i:Int):Boolean ={
        if(i==0&&c=='0'&&s=='0') true else false
      }
    fun3
    }
  func2
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

函数柯里化

def func1(i:Int)(s:String)(c:Char):Boolean={
    true
}
  • 1
  • 2
  • 3

4.6 闭包

闭包:函数式编程的标配

闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的 环境,称为闭包

def func1(s:String): Char=>(Int=>Boolean)={
    def func2(c:Char): Int=>Boolean ={
      def fun3(i:Int):Boolean ={
        if(i==0&&c=='0'&&s=='0') true else false
      }
    fun3
    }
  func2
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

当函数func1调用后弹出栈局部变量也会被释放,但fun3仍然可以访问到上层函数的局部变量

这是因为scala将局部变量创建了函数对象打成了包放在了堆内存里,

4.7 递归

def con(n:Int): Int = {
      if (n ==0) return 1
        con(n-1) * n
    }
//尾递归,节省栈空间,指针不用变化
def con1(n:Int):Int = {
    @tailrec //该注解可检查尾递归是否正确
    def loo(n:Int,sum:Int):Int = {
      if(n==0) return sum
        loo(n-1,sum * n)
    }
    loo(n,1)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

4.8 控制抽象

1)值调用:把计算后的值传递过去

 object TestControl {
 	def main(args: Array[String]): Unit = {
 	def f = ()=>{
 		println("f...")
		10
 }
 	foo(f())
 }
 def foo(a: Int):Unit = {
 	println(a)
 	println(a)
 }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2)名调用:把代码传递过去

object TestControl {
 def main(args: Array[String]): Unit = {
 def f = ()=>{
 println("f...")
 10
 }
 foo(f())
 }
//def foo(a: Int):Unit = {
 def foo(a: =>Int):Unit = {//注意这里变量 a 没有小括号了
 println(a)
 println(a)
 }
}
输出结果:
f...
10
f...
10 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

自己定义while循环

def myWhile(flag: => Boolean): (=> Unit)=>Unit= {
        def dowhile(con: => Unit):Unit = {
          con
          if (flag)
            dowhile(con)
        }
      dowhile
    }
    var n=10
    myWhile(n>=1){
      println(n)
      n-=1
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.9 懒加载

当函数返回值被声明为 lazy 时,函数的执行将被推迟,直到我们首次对此取值,该函 数才会执行。这种函数我们称之为惰性函数。

第 五 章 面向对象

5.1包说明(包语句)

Scala 有两种包的管理风格,一种方式和 Java 的包管理风格相同,每个源文件一个包(包 名和源文件所在路径不要求必须一致),包名用“.”进行分隔以表示包的层级关系,如 com.atguigu.scala。另一种风格,通过嵌套的风格表示层级关系,如下

package com{
package atguigu{
package scala{
}
}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

第二种风格有以下特点:

(1)一个源文件中可以声明多个 package

(2)子包中的类可以直接访问父包中的内容,而无需导包

5.2 包对象

Scala 中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对 应包下所有 class 和 object 的共享变量,可以被直接访问。

package object com{
val shareValue="share"
def shareMethod()={}
}
  • 1
  • 2
  • 3
  • 4

如采用嵌套方式管理包,则包对象可与包定义在同一文件中,但是要保证包对象 与包声明在同一作用域中。

5.3 导包说明

1)和 Java 一样,可以在顶部使用 import 导入,在这个文件中的所有类都可以使用。
2)局部导入:什么时候使用,什么时候导入。在其作用范围内都可以使用
3)通配符导入:import java.util._
4)给类起名:import java.util.{ArrayList=>JL}
5)导入相同包的多个类:import java.util.{HashSet, ArrayList}
6)屏蔽类:import java.util.{ArrayList =>,}
7)导入包的绝对路径:new root.java.util.HashMap

Scala 中的三个默认导入分别是
import java.lang._
import scala._
import scala.Predef._

5.4 类和对象

定义类

[修饰符] class 类{

}不加修饰符默认为public,加上public会报错

一个scala文件可以定义多个类

class student{
    //该注解默认生成set get
    @BeanProperty
    private var:String name = "1"
    //默认public 但底层都是private
    //假如想要属性为空 name = _ 相当于null 或0
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5.5 封装

Scala 中的 public 属性,底层实际为 private,并通过 get 方法(obj.field())和 set 方法 (obj.field_=(value))对其进行操作。所以 Scala 并不推荐将属性设为 private,再为其设置 public 的 get 和 set 方法的做法。但由于很多 Java 框架都利用反射调用 getXXX 和 setXXX 方 法,有时候为了和这些框架兼容,也会为 Scala 的属性设置 getXXX 和 setXXX 方法(通过 @BeanProperty 注解实现)。

在 Java 中,访问权限分为:public,private,protected 和默认。在 Scala 中,你可以通 过类似的修饰符达到同样的效果。但是使用上有区别。

(1)Scala 中属性和方法的默认访问权限为 public,但 Scala 中无 public 关键字。

(2)private 为私有权限,只在类的内部和伴生对象中可用。

(3)protected 为受保护权限,Scala 中受保护权限比 Java 中更严格,同类、子类可以 访问,同包无法访问。

(4)private[包名]增加包访问权限,包名下的其他类也可以使用

5.6 构造器

和 Java 一样,Scala 构造对象也需要调用构造方法,并且可以有任意多个构造方法。

Scala 类的构造器包括:主构造器和辅助构造器

class 类名(形参列表) { // 主构造器
 // 类体
 def this(形参列表) { // 辅助构造器
 }
 def this(形参列表) { //辅助构造器可以有多个...
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

说明:

(1)辅助构造器,函数的名称 this,可以有多个,编译器通过参数的个数及类型 来区分。

(2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。

(3)构造器调用其他另外的构造器,要求被调用构造器必须提前声明。

5.7 抽象类

(1)定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类

(2)定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性

(3)定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法

继承&重写

(1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明 为抽象类

(2)重写非抽象方法需要用 override 修饰,重写抽象方法则可以不加 override。

(3)子类中调用父类的方法使用 super 关键字

(4)子类对抽象属性进行实现,父类抽象属性可以用 var 修饰;

子类对非抽象属性重写,父类非抽象属性只支持 val 类型,而不支持 var。

因为 var 修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写

5.8 单例对象(伴生对象)

1)基本语法 object Person{ val country:String=“China” }

2)说明

(1)单例对象采用 object 关键字声明

(2)单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。

(3)单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。

apply 方法

通过伴生对象的 apply 方法,实现不使用 new 方法创建对象。

如果想让主构造器变成私有的,可以在()之前加上 private。

当使用 new 关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构 建对象时,调用的其实时伴生对象的 apply 方法。

5.9 特质(Trait)

就是java中的interface with来实现

基本语法

trait 特质名 {
 主体
}
  • 1
  • 2
  • 3

Scala 中的 trait 中即可以有抽象属性和方法,也可以有具体的属性和方法

当trait与类属性冲突时,需要重写属性,否则报错

scala都是动态绑定的

没有父类:class 类名 extends 特质 1 with 特质 2 with 特质 3 …

有父类:class 类名 extends 父类 with 特质 1 with 特质 2 with 特质 3

多个特质方法冲突

img

trait Ball {
 def describe(): String = {
 "ball"
 }
}
trait Color extends Ball {
 override def describe(): String = {
 "blue-" + super.describe()
 }
}
trait Category extends Ball {
 override def describe(): String = {
 "foot-" + super.describe()
 }
}
class MyBall extends Category with Color {
 override def describe(): String = {
 "my ball is a " + super.describe()
 }
}
object TestTrait {
 def main(args: Array[String]): Unit = {
 println(new MyBall().describe())
 }
}
//输出结果
//my ball isa blue-foot-ball
  • 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

img

5.9 类型检查和转换

(1)obj.isInstanceOf[T]:判断 obj 是不是 T 类型。

(2)obj.asInstanceOf[T]:将 obj 强转成 T 类型。

(3)classOf 获取对象的类名。

class Person{
}
object Person {
 def main(args: Array[String]): Unit = {
 val person = new Person
 //(1)判断对象是否为某个类型的实例
 val bool: Boolean = person.isInstanceOf[Person]
 if ( bool ) {
 //(2)将对象转换为某个类型的实例
 val p1: Person = person.asInstanceOf[Person]
 println(p1)
 }
 //(3)获取类的信息
 val pClass: Class[Person] = classOf[Person]
 println(pClass)
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

5.10 枚举类和应用类

枚举类:需要继承 Enumeration

应用类:需要继承 App

// 枚举类
object Color extends Enumeration {
 val RED = Value(1, "red")
 val YELLOW = Value(2, "yellow")
 val BLUE = Value(3, "blue")
}
// 应用类
object Test20 extends App {
 println("xxxxxxxxxxx");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

使用 type 关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名

type myString = String

第六章 集合

6.1 可变与不可变

Scala 的集合有三大类:序列 Seq、集 Set、映射 Map,所有的集合都扩展自 Iterable 特质。

对于几乎所有的集合类,Scala 都同时提供了可变和不可变的版本,分别位于以下两 个包

不可变集合:scala.collection.immutable

可变集合: scala.collection.mutable

Scala 不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而 不会对原对象进行修改。类似于 java 中的 String 对象

可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。类似 于 java 中 StringBuilder 对象

img

Array与String特殊,使用了隐式转换

Set、Map 是 Java 中也有的集合,Seq 是 Java 没有的,我们发现 List 归属到 Seq 了,但和List有不同

for 循环有一个 1 to 3,就是 IndexedSeq 下的 Range ,String 也是属于 IndexedSeq

经典的数据结构比如 Queue 和 Stack 被归属到 LinearSeq(线性序列)

Scala 中的 Map 体系有一个 SortedMap,说明 Scala 的 Map 可以支持排序

IndexedSeq 和 LinearSeq 的区别:

IndexedSeq 是通过索引来查找和定位,因此速度快,比如 String 就是一个索引集

LinearSeq 是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找

6.2 遍历

定义

定义:val arr1 = new Array[Int](10)

println(arr01.length) // 4
 //(2)数组赋值
 //(2.1)修改某个元素的值
 arr01(3) = 10
 //(2.2)采用方法的形式给数组赋值
 arr01.update(0,1)
 //(3)遍历数组
 //(3.1)查看数组
 println(arr01.mkString(","))
 //(3.2)普通遍历
 for (i <- arr01) {
 println(i)
 }
 //(3.3)简化遍历
 def printx(elem:Int): Unit = {
 println(elem)
 }
 arr01.foreach(printx)
 // arr01.foreach((x)=>{println(x)})
 // arr01.foreach(println(_))
 arr01.foreach(println)
 //(4)增加元素(由于创建的是不可变数组,增加元素,其实是产生新的数
组)
 println(arr01)
 val ints: Array[Int] = arr01 :+ 5
 println(ints)
 }

  • 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

第二种方式定义数组

val arr1 = Array(1, 2)

(1)在定义数组时,直接赋初始值

(2)使用 apply 方法创建数组对象

6.3 数组

val arr01 = ArrayBuffer[Any](3, 2, 5)
arr01.+=(4)
//(3.2)向数组最后追加数据
arr01.append(5,6)
//向指定的位置(0)插入数据
arr01.insert(0,7,8)
arr01.preappend(5,6)
//删除指定下标元素
arr.remove(0)
//从下标开始删除多个
arr.remove(1,3)
arr1.toBuffer //不可变数组转可变数组
arr2.toArray //可变数组转不可变数组
//从数组中移除 1 元素
arr -= 1 
//指定分隔符遍历
arr.mkString(", ")
//返回 length长度的range
a.indices
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

ArrayBuffer 是有序的集合,增加元素使用的是 append 方法(),支持可变参数

多维数组

val arr = Array.ofDim[Double](3,4)
//说明:二维数组中有三个一维数组,每个一维数组中有四个元素
  • 1
  • 2

6.4 列表

有顺序不能用构造器创建,底层是继承了一个抽象类

只能用apply

值不可改变,可添加

//apply方法创建
var list = List(0,1)
var list1 = Nil.::(1)
var list2 = 1 :: 2 :: 3 :: Nil
//两个类实现了List Nil空列表类 ::类 :: 方法会将元素添加到末尾并返回新的列表
//列表追加列表 :: 会返回嵌套列表 不是想要的追加元素
// ::: 此方法会合成一个完整的list 或者 ++ 
// ++ 会返回新的 ++=会覆盖第一个
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可变列表

ListBuffer

可new 可apply

new ListBuffer[Int]()
//不能提前定义长度
ListBuffer(1,2)
3 +=: 4 list += 25 += 25
  • 1
  • 2
  • 3
  • 4

6.4 集合

可变set与不可变set名字都一样,只是包名不同

set是特质,使用伴生对象的apply方法,无序

方法与list一致

val list1: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
val list2: List[Int] = List(4, 5, 6, 7, 8, 9, 10)
//(1)获取集合的头
println(list1.head)
//(2)获取集合的尾(不是头的就是尾)
println(list1.tail)
//(3)集合最后一个数据
println(list1.last)
//(4)集合初始数据(不包含最后一个)
println(list1.init)
//(5)反转
println(list1.reverse)
//(6)取前(后)n 个元素
println(list1.take(3))
println(list1.takeRight(3))
//(7)去掉前(后)n 个元素
println(list1.drop(3))
println(list1.dropRight(3))
//(8)并集
println(list1.union(list2))
//(9)交集
println(list1.intersect(list2))
//(10)差集
println(list1.diff(list2))
//(11)拉链 注:如果两个集合的元素个数不相等,那么会将同等数量的数据进行拉链,多余的数据省略不用 嵌套的时Tuple2类型
println(list1.zip(list2))
//(12)滑窗
list1.sliding(2, 5).foreach(println)
  • 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

6.5 Map

Map("a" -> 1)
var a :Map [String , String]  = Map()
map.get("a")
//不会返回value 会返回 Some(1) 想要返回value .get().get
//当没有key值时get会报异常 使用 getOrElse("c",0) 
//如果为null 赋值0
//也可以map("a") 报异常

map.put("a","b")
//删除a 键值对
map -= "a"
println(map)
map += "sczc" -> "s"
map.update("scac","nu")
map += (("1","1"))
println(map)
//合并map
map ++= mutable.Map ("2"-> "2")
println(map)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

6.6 元组

//元组 最大只有22个元素 创建方式跟py一样 () 下标从一开始不可变
//并不是Any类型的列表 每个元组长度都有一个单独类 Tuple1-22
var tuple = ("1",1,2.1,true)
println(tuple)
//(1,1,2.1,true)
//访问数据 tuple._number or tuple.produceElement(num)
println(tuple._1)
//迭代器遍历
for (elem <- tuple.productIterator)
println(elem)
//嵌套元组
var tuple2 = (1,2,(3,3))
println(tuple2._3._1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

集合常用函数

//(1)求和
println(list.sum)
//(2)求乘积
println(list.product)
//(3)最大值
println(list.max)
//(4)最小值
println(list.min)
//(5)排序
// (5.1)按照元素大小排序
println(list.sortBy(x => x))
// (5.2)按照元素的绝对值大小排序
println(list.sortBy(x => x.abs))
// (5.3)按元素大小升序排序
println(list.sortWith((x, y) => x < y))
// (5.4)按元素大小降序排序
println(list.sortWith((x, y) => x > y))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

(1)sorted 对一个集合进行自然排序,通过传递隐式的 Ordering

(2)sortBy 对一个属性或多个属性进行排序,通过它的类型。

(3)sortWith 基于函数的排序,通过一个 comparator 函数,实现自定义排序的逻辑。lamda表达式

集合计算高级函数

val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
 val nestedList: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
 val wordList: List[String] = List("hello world","hello atguigu", "hello scala")
 //(1)过滤
 println(list.filter(x => x % 2 == 0))
 //(2)转化/映射
 println(list.map(x => x + 1))
 //(3)扁平化
 println(nestedList.flatten)
 //(4)扁平化+映射 注:flatMap 相当于先进行 map 操作,在进行 flatten
操作
 println(wordList.flatMap(x => x.split(" ")))
 //(5)分组
 println(list.groupBy(x => x % 2))
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Reduce 方法

通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最 终获取结果。

val list = List(1,2,3,4)
 // 将数据两两结合,实现运算规则
 val i: Int = list.reduce( (x,y) => x-y )
 println("i = " + i)
 // 从源码的角度,reduce 底层调用的其实就是 reduceLeft
 //val i1 = list.reduceLeft((x,y) => x-y)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Fold 折叠:化简的一种特殊情况。

val list = List(1,2,3,4)
 // fold 方法使用了函数柯里化,存在两个参数列表
 // 第一个参数列表为 : 零值(初始值)
 // 第二个参数列表为: 简化规则
 // fold 底层其实为 foldLeft
 val i = list.foldLeft(1)((x,y)=>x-y)
 val i1 = list.foldRight(10)((x,y)=>x-y
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

多线程

(1 to 100).par.map()  //会多个x
  • 1

第七章 模式匹配

Scala 中的模式匹配类似于 Java 中的 switch 语法

var result = operator match {
 case '+' => a + b
 case '-' => a - b
 case '*' => a * b
 case '/' => a / b
 case _ => "illegal"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

每个 case 中,不需要使用 break 语句,自动中断 case。

匹配类型

def describe(x: Any) = x match {
 case i: Int => "Int"
 case s: String => "String hello"
 case m: List[_] => "List"
 case c: Array[Int] => "Array[Int]"
 case someThing => "something else " + someThing
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

匹配数组

scala 模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素 为 0 的数组。

val result = arr match {
 case Array(0) => "0" //匹配 Array(0) 这个数组
 case Array(x, y) => x + "," + y //匹配有两个元素的数
组,然后将将元素值赋给对应的 x,y
 case Array(0, _*) => "以 0 开头的数组" //匹配以 0 开头和
数组
 case _ => "something else"
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

匹配列表

def main(args: Array[String]): Unit = {
 val list: List[Int] = List(1, 2, 5, 6, 7)
 list match {
 case first :: second :: rest => println(first + "-" + 
second + "-" + rest)
 case _ => println("something else")
 }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

变量声明时匹配

var first :: second :: three = List(121,2 ,3.5,6)
println(s"$first  $second")


//for循环简化
for((var1,var2) <- List((1,2),(3,5)))
	println(var1+"   "+var2)
//可以不考虑位置的值 var2换成_ 只遍历第一个值
for((var1,_) <- List((1,2),(3,5)))
	println(var1)
//指定位置特殊值 只遍历1开头的元组
for((1,_) <- List((1,2),(3,5)))
	println(var1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

匹配对象及样例类

object User{
 def apply(name: String, age: Int): User = new User(name, age)
 def unapply(user: User): Option[(String, Int)] = {
 if (user == null)
 None
 else
 Some(user.name, user.age)
 }
}
object TestMatchUnapply {
 def main(args: Array[String]): Unit = {
 val user: User = User("zhangsan", 11)
 val result = user match {
 case User("zhangsan", 11) => "yes"
 case _ => "no"
 }
 println(result)
 }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

当将 User(“zhangsan”, 11)写在 case 后时[case User(“zhangsan”, 11) => “yes”],会默 认调用 unapply 方法(对象提取器),user 作为 unapply 方法的参数,unapply 方法 将 user 对象的 name 和 age 属性提取出来,与 User(“zhangsan”, 11)中的属性值进行 匹配

case 中对象的 unapply 方法(提取器)返回 Some,且所有属性均一致,才算匹配成功, 属性不一致,或返回 None,则匹配失败。

样例类

case class Person (name: String, age: Int)
  • 1

○1 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中 自动提供了一些常用的方法,如 apply、unapply、toString、equals、hashCode 和 copy。

○2 样例类是为模式匹配而优化的类,因为其默认提供了 unapply 方法,因此,样例 类可以直接使用模式匹配,而无需自己实现 unapply 方法。

○3 构造器中的每一个参数都成为 val,除非它被显式地声明为 var(不建议这样做)

偏函数中的模式匹配(了解)

img功能是返回输入的 List 集合的第二个元素

第八章 隐式转换

异常处理

def main(args: Array[String]): Unit = {
 try {
 var n= 10 / 0
 }catch {
 case ex: ArithmeticException=>{
 // 发生算术异常
 println("发生算术异常")
 }
 case ex: Exception=>{
 // 对异常处理
 println("发生了异常 1")
 println("发生了异常 2")
 }
 }finally {
 println("finally")
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

用 throw 关键字,抛出一个异常对象。所有异常都是 Throwable 的子类型。throw 表 达式是有类型的,就是 Nothing

当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用 于将类型进行转换,实现二次编译

隐式函数

class MyRichInt(val self: Int) {
 def myMax(i: Int): Int = {
 if (self < i) i else self
 }
 def myMin(i: Int): Int = {
 if (self < i) self else i
 }
}
object TestImplicitFunction {
 // 使用 implicit 关键字声明的函数称之为隐式函数
 implicit def convert(arg: Int): MyRichInt = {
 new MyRichInt(arg)
 }
def main(args: Array[String]): Unit = {
 // 当想调用对象功能时,如果编译错误,那么编译器会尝试在当前作用域范
围内查找能调用对应功能的转换规则,这个调用过程是由编译器完成的,所以称之为隐
式转换。也称之为自动转换
 println(2.myMax(6))
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

隐式参数

普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数,调用该方法时, 就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。

(1)同一个作用域中,相同类型的隐式值只能有一个

(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。

(3)隐式参数优先于默认参数

object TestImplicitParameter {
 implicit val str: String = "hello world!"
 def hello(implicit arg: String="good bey world!"): Unit = {
 println(arg)
 }
 def main(args: Array[String]): Unit = {
 hello
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

隐式类

隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶 级的。

object TestImplicitClass {
 implicit class MyRichInt(arg: Int) {
 def myMax(i: Int): Int = {
 if (arg < i) i else arg
 }
 def myMin(i: Int) = {
 if (arg < i) arg else i
 }
 }
 def main(args: Array[String]): Unit = {
 println(1.myMax(3))
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
implicit var a = 1
    
    def myTest():Unit = {
      println("---"+ implicitly[Int])
    }
  • 1
  • 2
  • 3
  • 4
  • 5

泛型

class MyList[+T]{ //协变 }

class MyList[-T]{ //逆变 }

class MyList[T] //不变

说明 协变:Son 是 Father 的子类,则 MyList[Son] 也作为 MyList[Father]的“子类”。

逆变:Son 是 Father 的子类,则 MyList[Son]作为 MyList[Father]的“父类”。

不变:Son 是 Father 的子类,则 MyList[Father]与 MyList[Son]“无父子关系”。

泛型上下限

Class PersonList[T <: Person]{ //泛型上限 }

Class PersonList[T >: Person]{ //泛型下限 }
Int): MyRichInt = {
new MyRichInt(arg)
}
def main(args: Array[String]): Unit = {
// 当想调用对象功能时,如果编译错误,那么编译器会尝试在当前作用域范
围内查找能调用对应功能的转换规则,这个调用过程是由编译器完成的,所以称之为隐
式转换。也称之为自动转换
println(2.myMax(6))
}
}


##  隐式参数

普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数,调用该方法时, 就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。



(1)同一个作用域中,相同类型的隐式值只能有一个 

(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。 

(3)隐式参数优先于默认参数

```scala
object TestImplicitParameter {
 implicit val str: String = "hello world!"
 def hello(implicit arg: String="good bey world!"): Unit = {
 println(arg)
 }
 def main(args: Array[String]): Unit = {
 hello
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

隐式类

隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶 级的。

object TestImplicitClass {
 implicit class MyRichInt(arg: Int) {
 def myMax(i: Int): Int = {
 if (arg < i) i else arg
 }
 def myMin(i: Int) = {
 if (arg < i) arg else i
 }
 }
 def main(args: Array[String]): Unit = {
 println(1.myMax(3))
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
implicit var a = 1
    
    def myTest():Unit = {
      println("---"+ implicitly[Int])
    }
  • 1
  • 2
  • 3
  • 4
  • 5

泛型

class MyList[+T]{ //协变 }

class MyList[-T]{ //逆变 }

class MyList[T] //不变

说明 协变:Son 是 Father 的子类,则 MyList[Son] 也作为 MyList[Father]的“子类”。

逆变:Son 是 Father 的子类,则 MyList[Son]作为 MyList[Father]的“父类”。

不变:Son 是 Father 的子类,则 MyList[Father]与 MyList[Son]“无父子关系”。

泛型上下限

Class PersonList[T <: Person]{ //泛型上限 }

Class PersonList[T >: Person]{ //泛型下限 }

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号