Scala详细教程

article/2025/10/14 14:04:01

Scala详细教程

目录

Scala详细教程

1.Scala 介绍

1.1 什么是 Scala Scala

 1.2 为什么要学 Scala

2.开发环境准备

2.1 ScalaSDK 安装

2.1.1Window 下安装 ScalaSDK

2.1.2Linux 下安装 ScalaSDK

2.2 IDEA 安装

2.3 IDEAScala 插件的离线安装

2.4 IDEA 创建 HelloScala 工程

3.基本语法

3.1 函数式编程体验 Spark-Shell 之 WordCount

​3.2 Scala 交互模式(cmd 窗口介绍)

3.3 数据类型

3.4 变量的定义

3.5 字符串的格式化输出

3.6 条件表达式

3.7 循环语句和yeild 关键字

3.8 运算符和运算符重载

3.9 方法的定义与调用

3.10 函数的定义与调用

3.11传值调用与传名调用

3.12 可变参数函数

3.13 默认参数值函数

3.14 高阶函数

3.15 部分参数应用函数

3.16 柯里化(Currying)

3.17 偏函数

4.集合的使用

4.1 定长数组和变长数组

4.2 map|flatten|flatMap|foreach 方法的使用

4.3 Seq 序列

4.4 Set 集

4.5  集合常用的方法

4.6并行化集合 par

4.7 Map 和 Option

4.8 案例 wordCount

5. 面向对象

5.1 scala 单例对象

5.2 scala 类

5.2.1 | 类定义 | 主构造器 | 辅助构造器

5.2.2 访问权限

5.2.3 伴生类 | apply 方法

5.3 Trait

5.4 抽象类

5.5 继承

5.5.1final 关键字

5.5.2 type 关键字

5.6 样例类/样例对象

6. 模式匹配 match case

6.1 匹配字符串/类型/守卫

6.2 匹配数组

6.3 匹配集合

6.4 匹配元组

6.5 匹配样例类/样例对象

7.Scala 高级语法

7.1 隐式(implicit)详解

7.1.1 隐式参数

7.1.2 隐式的转换类型

7.1.3 隐式类

7.2 泛型

7.3 类型约束

7.3.1 上界(UpperBounds)/下界(lowerbounds)

7.3.2 视图界定/上下文界定


 

1.Scala 介绍

1.1 什么是 Scala Scala

是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性 。Scala 运 行 于Java平台( Java 虚 拟 机 ), 并 兼 容 现 有 的 Java 程 序 。

 1.2 为什么要学 Scala

  1. 优雅:这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API 是否优 雅直接影响用户体验。
  2. 速度快:Scala 语言表达能力强,一行代码抵得上 Java 多行,开发速度快;Scala 是静态编 译的,所以和 JRuby,Groovy 比起来速度会快很多。
  3. 能融合到 Hadoop 生态圈:Hadoop 现在是大数据事实标准,Spark 并不是要取代 Hadoop, 而是要完善 Hadoop 生态。JVM 语言大部分可能会想到 Java,但 Java 做出来的 API 太丑,或者想实现一个优雅的 API 太费劲。

2.开发环境准备

2.1 ScalaSDK 安装

安装 ScalaSDK前 ,请 确 保 已 安 装 JDK1.8+.

2.1.1Window 下安装 ScalaSDK

访问 Scala 官网 https://www.scala-lang.org/download/all.html下载 Scala 编译器安装包,目前大多数的框架都是用 2.11.x 编写开发的,Spark2.x 使用的就是 2.11.x,所以这里推 荐 2.11.x 版本,下载 scala-2.11.8.msi 后点击下一步就可以了。scala-2.11.8.zip需要自己配置环境变量

2.1.2Linux 下安装 ScalaSDK

下载 Scala 地址https://www.scala-lang.org/download/2.12.8.html 然后解压 Scala 到 指定目录 tar -zxvfscala-2.11.8.tgz -C /usr/java 配置环境变量,将 scala 加入到 PATH 中 vi/etc/profile

exportJAVA_HOME=/usr/java/jdk1.8.0_111
exportPATH=$PATH:$JAVA_HOME/bin:/usr/java/scala-2.11.8/bin

 

2.2 IDEA 安装

目前 Scala 的开发工具主要有两种:Eclipse 和 IDEA,这两个开发工具都有相应的 Scala 插件, 如果使用 Eclipse,直接到 Scala 官网下载即可 http://scala-ide.org/download/sdk.html。
由 于 IDEA 的 Scala 插 件 更 优 秀 , 大 多 数 Scala 程 序 员 都 选 择 IDEA , 可 以 到 http://www.jetbrains.com/idea/download/下载社区免费版,点击下一步安装即可,安装时如 果有网络可以选择在线安装 Scala 插件。这里我们使用离线安装 Scala 插件:


1.安装 IDEA,点击下一步即可。由于我们离线安装插件,所以点击 SkipAllandSetDefaul 2.下载 IEDA 的 scala 插件,地址 http://plugins.jetbrains.com/?idea_ce

 

2.3 IDEAScala 插件的离线安装

安装 Scala 插件: Configure->Plugins->Installpluginfromdisk-> 选择 Scala 插件 ->OK-> 重启 IDEA

 

2.4 IDEA 创建 HelloScala 工程

安装完成后, 双击已打开 IDEA, 创建一个新的项目(CreateNewProject)

选中左侧的 Scala->IDEA->Next 

输入项目名称 -> 点击 Finish 完成即可

 

3.基本语法

3.1 函数式编程体验 Spark-Shell 之 WordCount

Q1:对 上 述 文 件 内 容 使 用 Spark进 行 单 词 个 数 统 计 ?


Q2:对 上 述 输 出 结 果 进 行 降 序?


3.2 Scala 交互模式(cmd 窗口介绍)

按住win+R,输入 cmd 命令, 打开 windows 控制台:

输入 scala, 进入 Scalashell 交互模式:

 

3.3 数据类型

Scala 和 Java 一样,有 7 种数值类型 Byte、Char、Short、Int、Long、Float 和 Double(无包 装类型)和 Boolean、Unit 类型.
注意:Unit 表示无值,和其他语言中 void 等同。用作不返回任何结果的方法的结果类型。Unit 只有一个实例值,写成()。

3.4 变量的定义

object 变量定义 extends App { /** * 定义变量使用var或者val关 键 字 ** 语法: *  var | val 变量名称(: 数据类型) =变量值*/// 使用val修饰的变量, 值不能为修改,相当于java中final修饰的变量 val name = "tom"// 使用var修饰的变量,值可以修改 var age = 18// 定义变量时,可以指定数据类型,也可以不指定,不指定时编译器会自动推测变量的数据类型 val name2 : String = "jack"
}

3.5 字符串的格式化输出

/** Scala 中的格式化输出*/object ScalaPrint extends App {val name = "JackMa"val price = 998.88dval url = "www.baidu.com"// 普通输出,注意这里是可以使用逗号分隔的,但是在java中,我们是需要用“+”号拼接println("name=" + name,"price="+price,"url="+url)// 'f'插值器允许创建一个格式化的字符串,类似于C语言中的printf。// 在使用'f'插值器时,所有变量引用都应该是printf样式格式说明符,如%d ,%i ,%f等 。// 这里$name%s打印String变量name,println(f"姓名:$name%s,价格:$price%1.2f,网址:$url%s")println(f"姓名:%%s,价格:%%1.1f,网址:%%s",name,price,url)// 's'插值器允许在字符串中直接使用变量// 下列语句中将String型变量($name)插入到普通字符串中println(s"name=$name,price=$price,url=$url")//'s'插值器还可以处理任意形式的表达式println(s"1+1=${1+1}") //output "1+1=2"
}

3.6 条件表达式

/** Scala if条件表达式*/
object ScalaIf extends App {//if语句的使用var faceValue=98var res1=if (faceValue>90) "帅的一批" else "有点恼火"print(res1)//3>5 不成立,且代码没有else分支,那么res2应该输出什么呢?var i=3var res2=if (i>5) iprint(res2)// output  ()代表空// 支持嵌套,if...else if ...else代码过多时可以使用{}val score=85if(score<60)"不及格"else if(score>=60&&score<70)"及格"else if (score>=80&&score<90)"优秀"else "优秀"
}

3.7 循环语句和yeild 关键字

/** Scala for循环*/
object ScalaFor extends App {//  定一个数组var arr=Array(1,2,3,4,5,6)//遍历数组中么个元素for(ele <- arr){print(ele)} /*output:1 2 3 4 5 6*/// 0 to 5 =>会生成一个范围集合Range(0,1,2,3,4,5),左闭右闭for(i <- 0 to 5){print(i) /*output:0,1,2,3,4,5*/}// 0 until 5 =>会生成一个范围集合Range(0,1,2,3,4),左闭右开for(i <- 0 until 5){print(i) /*output:0,1,2,3,4*/}// for循环中可以增加守卫,下面这段语句是打印arr数组中的偶数for(i <- arr if i%2==0){print(i)}/*input:2,4,6*///双层for循环for (i <- 1 to 3;j <- 1 to 3 if i!=i){print(i*10+j+"")}/*output:12,13,21,23,31,32*///  yield 关键字将满足条件的e的值又组合成一个数组var arr2=for(e <- arr if e%2==0)yield efor (i <- arr2){print(i)/*output:2,4,6*/}
}

3.8 运算符和运算符重载

Scala 中的+-*/%等操作符的作用与 Java 一样,位操作符 &|^>><<也一样。

只是有 一点特别的:这些操作符实际上是方法。

例如:

a+b

是如下方法调用的简写:

a.+(b)

a 方法 b 可以写成 a.方法(b)

3.9 方法的定义与调用

方法的返回值类型可以不写,编译器可以自动推断出来,但是对于递归函数,必须指定返回类型。

/** 方法的定义及调用* 定义方法的格式为 :* def methodName ([listofparameters]) : [ returntype ] = { }* 如果不使用等号和方法体,则隐式声明抽象(abstract)方法 。*/
object ScalaMethod extends App{// 定义一个sum方法,该方法有两个参数,返回值为int类型def sum(a:Int, b: Int): Int = { a + b }// 调用val result=sum(1,5)print(result)// 该方法没有任何参数 , 也没有返回值def sayHello1 = print("Say BB1")def sayHello2() = print("Say BB2")sayHello1 // 如果方法没有()调用时不能加()sayHello2() // 可以省略( ) ,也可以不省略
}

方法可转换为函数:

3.10 函数的定义与调用

函数定义方式一:

调用:f1(2),其中 f1 为函数的引用,也可以叫做函数名。function1 表示一个参数的函数。


函数定义方式二:

下面为没有任何参数的函数,,函数的返回值为 Int 类型。

3.11传值调用与传名调用

通常,函数的参数是传值参数;也就是说,参数的值在传递给函数之前就已经确定。

但是, 在 Scala 中,我们方法或者函数的参数还可以是一个表达式,也就是将一个代码逻辑传递给了某个方法或者函数。

/**scala的*      传名调用( call-by-name)*      传值调用( call-by-value)*/
object ScalaCallName  extends App{def currentTime():Long={System.nanoTime();}// 该方法的参数为一个无参的函数,并且函数的返回值为Longdef delayed(f: => Long): Unit ={print(s"time=${f}")}def delayed2(time: Long): Unit ={print(s"time=${time}")}//调用方式一delayed(currentTime())// 调用方式二// 等价于调用方式一,本质是先计算出参数的值,在带入方法var time=currentTime()delayed2(time)}

3.12 可变参数函数

/**scala的可变参数 */
object ScalaVarParams  extends App{//定义一个可变参数方法def methodManyParams(params:Int*): Unit ={for (i <- params){print(i)}}// 调用methodManyParams(1,2,3,5)
}

3.13 默认参数值函数

/**scala的默认参数*/
object ScalaDefaultParams  extends App{//定义一个默认参数方法def add(a:Int=1,b:Int=2): Int ={a+b}// 等价于add(a=3,b=2)add(3)// 等价于add(a=4,b=5)add(4,5)// 等价于add(a=1,b=5)add(b=5)
}

3.14 高阶函数

/**scala的高阶函数*/
object ScalaHM  extends App{//  高阶函数将其他函数作为参数def apply(f:Int => Int,p:Int): Unit ={print(f(p))}def fn1(a:Int): Int ={a*a}apply(fn1,10)/*output:100*/
}

3.15 部分参数应用函数

如果函数传递所有预期的参数,则表示已完全应用它。 如果只传递几个参数并不是全部参 数,那么将返回部分应用的函数。这样就可以方便地绑定一些参数,其余的参数可稍后填写 补上。

/**scala的部分参数*/
object ScalaPartParams  extends App{//定义了一个求和函数def sum(a:Int,b:Int): Int ={a+b}// 调用sum函数的时候,传入了一个参数a=10,但是b为待定参数。// sumWithTen形成了一个新的函数,参数个数为一个,类型为int型,def sumWithTen:Int => Int=sum(10,_: Int)//sumWithTen(1),本质是sum(10,1)print(sumWithTen(1))
}

3.16 柯里化(Currying)

柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新 的函数返回一个以原有第二个参数为参数的函数。

/**scala的柯里化*/
object ScalaCurrying  extends App{//定义了一个求和函数def add(a:Int,b:Int)=a+b// 那么我们在调用的时候,应该是add(1,2)// 现在我们将这个函数变形def add(a:Int)(b:Int)=a+b// 那么我们在调用的是后应该是add(1)(2),其结果还是等于3,这种方式(或过程)就叫做柯里化,// 经过柯里化之后函数通用性降低,但是适用性有所提高//分析下其演变过程def add(a:Int): Int => Int={(b:Int)=>a+b}//(b:Int)=>a+b 为一个匿名函数,也就意味着add方法的返回值为一个匿名函数,// 那么现在的调用过程为var result=add(1)var sum1=result(2)}

3.17 偏函数

被包在花括号内没有 match 的一组 case 语句是一个偏函数,它是 PartialFunction[A,B]的一个 实例,A 代表参数类型,B 代表返回类型,常用作输入模式匹配。

/**scala的偏函数*/
object ScalaPartialFunc  extends App{/**PartialFunction[A,B],A 代表参数类型,B 代表返回类型,常用作输入模式匹配。 */def fun1:PartialFunction[String,Int]={case "one" => 1case "two" => 2case _ => -1}
}

4.集合的使用

Scala 的集合有三大类:序列 Seq、集 Set、映射 Map,所有的集合都扩展自 Iterable 特质 在 Scala 中集合有可变(mutable)和不可变(immutable)两种类型,immutable 类型的集合 初始化后就不能改变了(注意与 val 修饰的变量进行区别。

4.1 定长数组和变长数组

/**scala数组*/
import scala.collection.mutable.ArrayBuffer
object ScalaArray{def main(args: Array[String]): Unit = {// 初始化一个长度为8的定长数组,所有元素均为0val arr=new Array[Int](8)// 直接打印数组的到的是数组的hashcode值print(arr)// 将数组转换成数组缓冲,就可以看到数组中内容print(arr.toBuffer)// 定义一个长度为3的定长数组val arr2=Array("tom","jack","oliver")// 通过索引获取数组中的值print(arr2(1))// 创建一个变长数组var arr3=ArrayBuffer[Int]()// 末尾追加1arr3+=1//  追加多个元素arr3+=(2,3,4,5)//  追加一个数组arr3++=Array(6,7)//  追加一个变长数组arr3++=ArrayBuffer(8,9)//  打印arr3.foreach(print)// 在某个位置插入元素arr3.insert(0,-1,0)//  移除指定索引元素arr3.remove(0,1)}
}

4.2 map|flatten|flatMap|foreach 方法的使用

object ScalaArray{def main(args: Array[String]): Unit = {val arr=Array(1,2,3,4,5)// map函数将arr数组中所有元素进行某种映射操作,// (x:Int)=>x*2为一个匿名函数,x就是数组中的每个元素val z= arr map((x:Int)=>x*2)// 或者这样写,编译器会推断数据的类型val y=arr.map(x=>x*2)// 亦或者,_表示入参,代表数组中每个元素val x=arr.map(_ * 2)println("--------骚气分割线-----------")val words=Array("tom jack oliver jack","hello tom oliver tom ")// 将words中元素按照“,”切分val splitWords: Array[Array[String]] = words.map(x=>x.split(","))//此时数组中的每个元素进行split操作之后变成了Array,// flatten是对splitWords里面的元素进行扁平化操作val flatten: Array[String] = splitWords.flatten// 上诉两步操作可以等价于flatmap,意味着先map操作之后进行flatten操作val result=words.flatMap(_ .split(","))// 遍历数组,打印每个元素result.foreach(print)}
}

4.3 Seq 序列

不可变的序列 importscala.collection.immutable._

在 Scala 中列表要么为空(Nil 表示空列表)要么是一个 head 元素加上一个 tail 列表。 9::List(5,2) :: 操作符是将给定的头和尾创建一个新的列表

object ScalaListTest{def main(args: Array[String]): Unit = {// 创建一个不可变集合val list=List(1,2,3)// 创建一个可变集合val mutalist=ListBuffer(1,2,3)// 将0插入到list前面生成一个新的listval list1: List[Int] = list.:+ (0)val list2: List[Int] = 0+:listval list3: List[Int] = 0::listval list4: List[Int] = list.::(0)// 将一个元素添加到list后面,形成新的list5val list5: List[Int] = list:+(4)val list6=List(4,5,6)// 将两个list合并成一个新的listval list7: List[Int] = list++list6val list8: List[Int] = list ++: list6// 将list6插入到list前面形成一个新的集合val list9: List[Int] = list.:::(list6)list8.foreach(println)}
}

4.4 Set 集

可变的set

object ScalaSetTest{def main(args: Array[String]): Unit = {// 创建一个可变setval set: mutable.HashSet[Int] = new mutable.HashSet[Int]()// 往set中加入元素1set+=1// add等价于+=set.add(2)// 添加另一个set中元素set ++= Set(7,8,9)// 移除一个元素set-=2// remove等价于-=set.remove(2)set.foreach(print)}
}

不可变的set

object ScalaSetTest{def main(args: Array[String]): Unit = {// 创建一个不可变setval set: HashSet[Int] = new HashSet[Int]()// 将元素和原来的set合并生成一个新的set2,原有set不变val set2=set+1// 两个set合并生成一个新的setval set3=set++Set(4,5,6)set3.foreach(print)}
}

4.5  集合常用的方法

map, flatten, flatMap, filter, sorted, sortBy, sortWith, grouped, fold(折叠), foldLeft, foldRight, reduce, reduceLeft, aggregate, union, intersect(交集), diff(差集), head, tail, zip, mkString, foreach, length, slice, sum

scala> val list=List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)scala> list.map(x=>x*2)
res0: List[Int] = List(2, 4, 6, 8, 10)scala> list.sortBy(x=> -x)
res5: List[Int] = List(5, 4, 3, 2, 1)scala> val words=List(("a",3),("b",5),("c",2))
words: List[(String, Int)] = List((a,3), (b,5), (c,2))scala> words.sortBy(t=> t._2)
res6: List[(String, Int)] = List((c,2), (a,3), (b,5))scala> words.sortWith((x,y)=>x._2>y._2)
res7: List[(String, Int)] = List((b,5), (a,3), (c,2))scala> list.grouped(2).toList
res10: List[List[Int]] = List(List(1, 2), List(3, 4), List(5))scala> list
res13: List[Int] = List(1, 2, 3, 4, 5)scala> list.fold(0)((x,y)=>x+y)
res14: Int = 15scala> list.fold(0)(_+_)
res15: Int = 15scala> list.foldLeft(2)(_ - _)
res18: Int = -13scala> list.foldRight(2)(_ - _)
res19: Int = 1scala> list.reduce((x,y)=> x+y)
res20: Int = 15scala> list.aggregate(0)(_ + _,_ + _)
res21: Int = 15scala> val list=List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)scala> val list2=List(1,3,5,7,8)
list2: List[Int] = List(1, 3, 5, 7, 8)scala> list.union(list2)
res0: List[Int] = List(1, 2, 3, 4, 5, 1, 3, 5, 7, 8)scala> list.intersect(list2)
res1: List[Int] = List(1, 3, 5)scala> list.diff(list2)
res2: List[Int] = List(2, 4)scala> list.head
res3: Int = 1scala> list.tail
res4: List[Int] = List(2, 3, 4, 5)scala> list.zip(list2)
res5: List[(Int, Int)] = List((1,1), (2,3), (3,5), (4,7), (5,8))scala> list.mkString("|")
res7: String = 1|2|3|4|5scala> list.foreach(print)
12345scala> list.length
res11: Int = 5scala> list.slice(1,3)
res12: List[Int] = List(2, 3)scala> list.sum
res14: Int = 15

4.6并行化集合 par

调用集合的 par 方法, 会将集合转换成并行化集合

object ScalaParTest{def main(args: Array[String]): Unit = {val list: List[Int] = List(1,7,9,8,0,3,5,4,6,2)val par: ParSeq[Int] = list.par}
}

4.7 Map 和 Option

在Scala中Option类型样例类用来表示可能存在或也可能不存在的值(Option的子类有Some 和 None)。Some 包装了某个值,None 表示没有值。

object ScalaMapTest{def main(args: Array[String]): Unit = {val map: Map[String, Int] = Map("a"->1,"b"->2,"c"->3)val i: Int = map("d")// map的get方法返回值Option,意味着maybeInt可能取到值也可能没有取到值val maybeInt: Option[Int] = map.get("d")// 如果maybeInt=None时会抛出异常val v=maybeInt.get// 第一个参数为要获取的key// 第二个参数为如果没有这个key返回一个默认值val ele: Int = map.getOrElse("d",-1)}
}

4.8 案例 wordCount

object ScalaWordCount{def main(args: Array[String]): Unit = {// 第一种方式val words=Array("hello tom jack oliver","tom jack jim hello")val strings: Array[String] = words.flatMap(_ .split(" "))val tuples: Array[(String, Int)] = strings.map((_ ,1))val stringToTuples: Map[String, Array[(String, Int)]] = tuples.groupBy(_._1)val stringToInt = stringToTuples.mapValues(_.length)stringToInt.foreach(print)// 第二种方式val list= words.flatMap(_.split(" "))//对数组中每个元素进行切分,并进行扁平化操作.map((_,1))    // 将数组中每个元素转换成元组,元组第二个元素为1.groupBy(_._1) // 将相同单词元组分为一组.mapValues(_.length)//对每个key的value集合进行长度操作.toList //  转换成listlist.foreach(print)//  第三种方式val list2: List[(String, Int)] = words.flatMap(x=>x.split(" ")).groupBy(x=>x).map(t=>(t._1,t._2.length)).toListlist2.foreach(print)}
}

5. 面向对象

5.1 scala 单例对象

在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那 就是使用关键字 object,object 对象不能带参数。

/** 单例对象*/
object ScalaSingleton{def saySomeThing(msg:String): Unit ={println(msg)}
}object test{def main(args: Array[String]): Unit = {ScalaSingleton.saySomeThing("hello")println(ScalaSingleton)println(ScalaSingleton)// output:// hello//ScalaSingleton$@ea4a92b//ScalaSingleton$@ea4a92b}
}

5.2 scala 类

5.2.1 | 类定义 | 主构造器 | 辅助构造器

类定义

/** scala中,类并不用声明为public* 如果你没有定义构造器,类会有一个默认的空参构造器** var 修饰的变量,对外提供setter、getter方法* val 修饰的变量,对外只提供getter方法,不提供setter方法*/
class Student{// _表示一个占位符,编译器会根据你变量的类型赋予相应的初始值var name:String=_// 错误代码,val修饰的变量不能使用占位符// val age:Int=_val age:Int=10
}object test{def main(args: Array[String]): Unit = {// 空参构造器可以加()也可以不加val student = new Student()student.name="JackMa"//  错误代码,类中使用val修饰的变量不能更改//  student.age=20println(s"name=${student.name},age=${student.age}")}
}

定义在类后面的为类主构造器, 一个类可以有多个辅助构造器

/** 定义在类名称后面的构造器为主构造器* 类的主构造器中的属性会定义成类的成员变量* 类的主构造器中属性没有被var|val修饰的话,该属性不能访问,相当于对外没有提供get方法* 如果属性使用var修饰,相当于对外提供set和get方法*/
class Student1(var name:String,val age:Int){var gender:String=_def this(name:String,age:Int,gender:String){this(name,age)this.gender=gender}
}object test{def main(args: Array[String]): Unit = {val s1 = new Student1("Tom",18)println(s"name=${s1.age},age=${s1.age},gender=${s1.gender}")val s2 = new Student1("JackMa",20,"man")println(s2.gender)// output:// name=18,age=18,gender=null// man}
}

5.2.2 访问权限

  • 构造器的访问权限
/** private 加在主构造器之前,这说明该类的主构造器是私有的,外部对象或者外部类不能访问* 也适用与辅助构造器*/
class Student2 private(var name:String,val age:Int){var gender:String=_private def this(name:String,age:Int,gender:String){this(name,age)this.gender=gender}
}
  • 成员变量的访问权限
/** private val age* age 在本类中有setter和getter方法* 但是加上private 也就意味着age只能在这个类的内部及其伴生类中可以修改*/
class Student3 private(){private var name:String=_// 伴生类可以访问private var age:Int=_// private [this]关键字标识给属性只能在类内部访问,伴生类不能访问private [this] var gender:String="man"
}
// 伴生类
object Student3{def main(args: Array[String]): Unit = {val student = new Student3()// 伴生对象可以访问student.name="jack"student.age =20println(s"name=${student.name},age=${student.age}")// 伴生类不能访问//println(student.gender)// output:// name=jack,age=20}
}
  • 类包的访问权限
/** private [this] class放在类声明最前面,是修饰类的访问权限,也就是说类在某些包下可见或不能访问* private [sheep] class代表该类在sheep包及其子包下可见,同级包不能访问*/
private [this] class Student4 (val name:String,private var age:Int){var gender :String=_
}
// 伴生类
object Student4{def main(args: Array[String]): Unit = {val s = new Student4("JackMa",18)print(s.age)}
}

5.2.3 伴生类 | apply 方法

在 Scala 中, 当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象。必须 在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类。类和它的伴生对象可以互相访问其私有成员。

/** 定义一个apply方法* object对象中可以对apply进行各种重载*/
object test{def main(args: Array[String]): Unit = {val v = test(2,3)// 语法糖print(v)//output:5}def apply(a:Int,b:Int)={a+b}
}

5.3 Trait

ScalaTrait(特质) 相当于 Java 的接口,实际上它比接口还功能强大。 与接口不同的是,它还可以定义属性和方法的实现。 一般情况下 Scala 的类只能够继承单一父类,但是如果是 Trait(特质) 的话就可以继承多个,实现了多重继承。使用的关键字是 trait。

trait ScalaTrail {// 定义了一个属性var pro:Int=666// 定义一个没有实现的方法def sayHello(name:String)// 定义了一个带具体实现的方法def small(name:String): Unit ={println(s"太阳对${name}笑")}
}object ScalaTrailImpl extends ScalaTrail {// 实现方法时可以有override关键字,也可以没有def sayHello(name: String): Unit = {println(s"hello $name")}// 重写方法时必须得有override关键字override def small(name: String): Unit = {println(s"$name like small")}
}object test extends App{ScalaTrailImpl.sayHello("Oliver")// 如果ScalaTrailImpl没有重写small方法,则调用ScalaTrail中已经实现了的方法// 如果ScalaTrailImpl重写了small方法,则调用的是ScalaTrailImpl中的方法ScalaTrailImpl.small("wang")// output:// hello Oliver// wang like small
}

动态混入特质,使用with关键字

trait ScalaTrail {// 定义了一个属性var pro:Int=666// 定义一个没有实现的方法def sayHello(name:String)// 定义了一个带具体实现的方法def small(name:String): Unit ={println(s"太阳对${name}笑")}
}
class Student {}object test extends App{val student: Student with ScalaTrail = new Student with ScalaTrail {override def sayHello(name: String): Unit = {println(f"name=$name")}}student.sayHello("Jack")
}

5.4 抽象类

在 Scala 中, 使用 abstract 修饰的类称为抽象类. 在抽象类中可以定义属性、未实现的方法和 具体实现的方法。


abstract class Animal{// 定义了一个属性var name:String="animal"// 定义一个未实现方法def sleep()// 定义一个带具体实现方法def eat(f:String): Unit ={println(s"eating $f")}
}

5.5 继承

继承是面向对象的概念,用于代码的可重用性。 被扩展的类称为超类或父类, 扩展的类称 为派生类或子类。Scala 可以通过使用 extends 关键字来实现继承其他类或者特质。

/** with 后面只能是特质* 父类未实现的方法,子类必须实现* 父类已经实现的方法,子类要重写该方法,必须使用override关键字*/
abstract class Animal{// 定义了一个属性var name:String="animal"// 定义一个未实现方法def sleep()// 定义一个带具体实现方法def eat(f:String): Unit ={println(s"eating $f")}
}
class Dog extends Animal {override def sleep(): Unit = {println("耳朵贴地睡")}
}
object test extends App{private val dog = new Dogdog.sleep();dog.eat("bone")// output:// 耳朵贴地睡// eating bone
}

5.5.1final 关键字

 

  • 被 final 修饰的类不能被继承;
  • 被 final 修饰的属性不能重写;
  • 被 final 修饰的方法不能被重写。

5.5.2 type 关键字

Scala 里的类型,除了在定义 class,trait,object 时会产生类型,还可以通过 type 关键字来声明 类型。
type 相当于声明一个类型别名:

object test extends App{// 把String类型用S代替type S = Stringvar name : S= "Oliver"println(name)
}

通常 type 用于声明某种复杂类型,或用于定义一个抽象类型。

class A{type Tdef fn(t:T): Unit ={println(t)}
}
class B extends A{override type T = Int
}
class C extends A{override type T = String
}
object test extends App{private val b = new B()private val c = new C()b.fn(3)c.fn("Hello")
}

5.6 样例类/样例对象

/** 样例类,使用case关键字修饰的类,其重要的就是支持模式匹配* 样例类:case class 类名(属性)* 类名定义必须是驼峰式,属性名称第一个字母小写*/
case class Message(sender:String,massageContent:String)
/** 样例对象不能封装数据*/
case object CheckHeartBeat

 

6. 模式匹配 match case

6.1 匹配字符串/类型/守卫

object test extends App{/** 匹配字符串*/def contentMatch(str:String)=str match {case "dog"=>println("小狗")case "cat"=>println("小猫")case "1"=>println("数字1")case _ => println("匹配失败")}contentMatch("cat")contentMatch("1")contentMatch("2")//  output://  小猫//  数字1//  匹配失败/** 匹配类型*/def typeMatch(ele:Any)=ele match {case x:Int=>println(s"Int:${x}")case y:Double=>println(s"Double:${y}")case z:String=>println(s"String:${z}")case _ =>println("match failure")}typeMatch("helo")typeMatch(2)typeMatch(2d)//  output://  String:helo//  Int:2//  Double:2.0

6.2 匹配数组

object test extends App{/** 匹配数组*/def arrayMatch(arr:Any)=arr match {case Array(0)=>println("只有一个0元素的数组")case Array(0,_)=>println("以0开头,拥有两个元素的数组")case Array(1,_,3)=>println("以1开头,3结尾的任意三个元素的数组")case Array(_*)=>println("N个元素的数组")}arrayMatch(Array(0))arrayMatch(Array(0,2))arrayMatch(Array(1,true,3))arrayMatch(Array(1,3,5,7,9))// output:// 只有一个0元素的数组// 以0开头,拥有两个元素的数组// 以1开头,3结尾的任意三个元素的数组// N个元素的数组
}

6.3 匹配集合

object test extends App{/** 匹配集合*/def listMatch(list:Any)=list match {case 0::Nil=>println("只有0元素的集合")case 7::9::Nil=>println("只有7和9两个元素的集合")case x::y::z::Nil=>println(s"只有三个元素集合${x},${y},${z}")case m::n=>println(s"拥有head和tail的集合。head:${m},tail:${n}")}listMatch(List(0))listMatch(List(7,9))listMatch(List(1,2,3))listMatch(List(8,7,6,5,4))// output:// 只有0元素的集合// 只有7和9两个元素的集合// 只有三个元素集合1,2,3// 拥有head和tail的集合。head:8,tail:List(7, 6, 5, 4)
}

6.4 匹配元组

object test extends App{/** 匹配元组*/def tupMatch(tup:Any)=tup match {case (3,x,y)=>println("第一个元素为3的元组")case (_,2)=>println("第二个元素为2,拥有两个元素的数组")case (x,y,z)=>println("拥有三个元素的任意元组")}tupMatch((3,2,1))tupMatch((3,2))tupMatch((4,2,1))//  output://  第一个元素为3的元组//  第二个元素为2,拥有两个元素的数组//  拥有三个元素的任意元组
}

6.5 匹配样例类/样例对象

case object CheckTimeOutTask
case class SubmitTask(id:String,name:String)
case class HeartBeat(time:Long)
object test extends App{/** 匹配样例类,样例对象*/def coMatch(ele:Any)=ele match {case SubmitTask(id,name)=>println(s"submit task-id:${id},task-name:${name}")case CheckTimeOutTask=>println("checking.....")case HeartBeat(time)=>println(s"time is ${time}")}coMatch(SubmitTask("001","node1"))coMatch(CheckTimeOutTask)coMatch(HeartBeat(8888888L))coMatch(HeartBeat(6666))// output:// submit task-id:001,task-name:node1// checking.....// time is 8888888// time is 6666
}

 

 

7.Scala 高级语法

 

7.1 隐式(implicit)详解

  • 隐式参数 
  • 隐式转换类型
  • 隐式类

7.1.1 隐式参数

定义方法时,可以把参数列表标记为 implicit,表示该参数是隐式参数。一个方法只会有一 个隐式参数列表,置于方法的最后一个参数列表。如果方法有多个隐式参数,只需一个 implicit 修饰即可。
譬如:deffire(x:Int)(implicita:String,b:Int=9527)
当调用包含隐式参数的方法是,如果当前上下文中有合适的隐式值,则编译器会自动为该 组参数填充合适的值,且上下文中只能有一个符合预期的隐式值。如果没有编译器会抛出 异常。当然,标记为隐式参数的我们也可以手动为该参数添加默认值。

object test extends App{def say(implicit content:String)=println(content)/** say方法时隐式参数,如果你没有传参数* 编译器在编译的时候会自动的从当前上下文中找一个隐式值(符合参数类型的隐式值)*/say("good morning")implicit val content="Are you ok ?"say//  output://  good morning//  Are you ok ?}

7.1.2 隐式的转换类型

使用隐式转换将变量转换成预期的类型是编译器最先使用 implicit 的地方。当编译器看到类 型 X 而却需要类型 Y,它就在当前作用域查找是否定义了从类型 X 到类型 Y 的隐式定义。

object test extends App{//var i:Int=3.14/*此时程序会报错*/implicit def double2Int( d:Double)=d.toInt// i是int类型,但是赋值的时候缺是一个浮点型,此时编译器会在当前上下文中找一个隐式转换,// 找一个double转换成int的隐式转换var i:Int=3.14println(i)}

7.1.3 隐式类

在 Scala 中,我们可以在静态对象中使用隐式类。


object test extends App{implicit class Calculator(x:Int){def add(y:Int)={x+y}}var z:Int=5println(5.add(3))
}

7.2 泛型

通俗的讲,比如需要定义一个函数,函数的参数可以接受任意类型。我们不可能一一列举所 有的参数类型重载函数。

那么程序引入了一个称之为泛型的东西,这个类型可以代表任意的数据类型。

例如 List,在创建 List 时,可以传入整形、字符串、浮点数等等任意类型。那是因为 List 在 类定义时引用了泛型

/**scala枚举类型*/
object ClothesEnum extends Enumeration{type ClothesEnum=Valueval 衣服,裤子,卫衣 =Value
}
/** 定义了一个泛型类*/
class Massage[T]{def getMassage(s:T): Unit ={println(s)}
}
/** 子类继承指明类型*/
class strMassage[String] extends Massage{}
object test extends App{private val msg = new Massage[String]msg.getMassage("有内鬼,终止交易")
}

7.3 类型约束

7.3.1 上界(UpperBounds)/下界(lowerbounds)

  • UpperBounds

在 Java 泛型里表示某个类型是 Test 类型的子类型,使用 extends 关键字:

<T extends Test>//或用通配符的形式: 
<? extends Test>

这种形式也叫 upperbounds(上限或上界),同样的意思在 Scala 的写法为:

[T <: Test]//或用通配符: 
[_ <: Test]

 

  • LowerBounds

在 Java 泛型里表示某个类型是 Test 类型的父类型,使用 super 关键字:

<T super Test>//或用通配符的形式: 
<? super Test>

这种形式也叫 lowerbounds(下限或下界),同样的意思在 scala 的写法为:

[T >: Test]//或用通配符: 
[_ >: Test]

7.3.2 视图界定/上下文界定

  • Viewbounds

<% 的意思是“viewbounds”(视界),它比  <: 适用的范围更广,除了所有的子类型,还允许隐式转换过去的类型。

def method[A <% B](arglist):R=...等价于:
def method[A](arglist)(implicit viewAB:A=>B):R=...
或等价于: 
implicit def conver(a:A):B= …
<% 除了方法使用之外,class 声明类型参数时也可使用: 
class A[T<%Int]
  • Contextbounds

与 viewbounds 一样 contextbounds(上下文界定)也是隐式参数的语法糖。为语法上的方便, 引入了”上下文界定”这个概念。

 


http://chatgpt.dhexx.cn/article/Wmgwm1Uj.shtml

相关文章

Scala教程

1. 基础语法 Scala语言是基于Java虚拟机运行的&#xff0c;所以基本的语法和Java是没有区别的。但是为了简化Java的开发以及融合其他的编程语言的优点和特性&#xff0c;Scala在基本的语法上做了大量的修改和优化&#xff0c;让程序员开发起来更简单&#xff0c;方便&#xff…

Scala教程-详细全部

版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明。 本文链接&#xff1a;https://blog.csdn.net/qq_39346903/article/details/113562823 文章目录 第1章 Scala入门1.1 概述1.1.1 为什么学习Scala1.…

单工,半双工和全双工有何区别和联系?

单工、半双工和全双工是电信计算机网络中的三种通信信道。这些通信信道可以提供信息传达的途径。通信信道可以是物理传输介质或通过多路复用介质的逻辑连接。物理传输介质是指能够传播能量波的材料物质&#xff0c;例如数据通信中的导线。并且逻辑连接通常指电路交换连接或分组…

单工、半双工及全双工之间的区别

1、单工数据传输只支持数据在一个方向上传输&#xff1b;在同一时间只有一方能接受或发送信息&#xff0c;不能实现双向通信&#xff0c;举例&#xff1a;电视&#xff0c;广播。 2、半双工数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实…

全双工、半双工、单工有什么区别!

什么是全双工&#xff1f; 全双工&#xff1a;&#xff08;Full Duplex&#xff09;是通讯传输的一个术语。通信允许数据在两个方向上同时传输&#xff0c;它在能力上相当于两个单工通信方式的结合。全双工指可以同时&#xff08;瞬时&#xff09;进行信号的双向传输&#xff0…

三种通信方式——单工、半双工和双工通信

数据通常是在两个站&#xff08;点对点&#xff09;之间进行传输&#xff0c;按照数据流的方向可分为三种传输模式&#xff1a;单 工、半双工、全双工。 一、单工通信&#xff08;simplex&#xff09; 单工通信只支持信号在一个方向上传输&#xff08;正向或反向&#xff09…

图文并茂~ 详解交换机中的半双工与全双工网络知识

很多学习网络知识的朋友在配置交换机时&#xff0c;时常会看到半双工与全双工的功能&#xff0c;今天带大家一起来了解下什么是半双工与全双工。 早期的网络设备HUB&#xff08;集线器&#xff09;就是半双工&#xff0c;目前基本没有人用了&#xff0c;而现在用的SWITCH&…

[网络管理]全双工与半双工的区别

同事说新办公室的网络一直不稳定&#xff0c;经常掉线延迟。检查进口线路和更换转接网线。都无法解决问题。 估计是不是进口网线中一根或者2根短路&#xff0c;那就修改下网卡属性吧。把自适应改成全双工10M模式&#xff0c;测试OK。 全双工传输 英文写法是:Full&#xff0d;Du…

单工通信、半双工通信和全双工通信

数据通信中&#xff0c;数据在线路上的传送方式可以分为 单工通信、 半双工通信和 全双工通信三种。 ewlw半双工通信&#xff1a;半双工通信是指数据可以沿两个方向传送&#xff0c;但同一时刻一个 半双工总线结构 信道只允许单方向传送&#xff0c;因此又被称为双向交替通信。…

比较全面的HTTP和TCP网络传输的单工、全双工和半双工

文章目录 单工、全双工、半双工1. 单工2. 半双工3. 全双工 HTTP协议的工作模式TCP协议的工作模式 本文参考&#xff1a; 图解网络传输单工、半双工、全双工 - 知乎 (zhihu.com) 问&#xff1a;HTTP是单工的还是双工的还是半双工的 - 简书 (jianshu.com) 关于TCP全双工模式的解释…

简析全双工与半双工的区别

全双工传输英文写法是:Full&#xff0d;Duplex Transmissions 是指交换机在发送数据的同时也能够接收数据&#xff0c;两者同步进行&#xff0c;这好像我们平时打电话一样&#xff0c;说话的同时也能够听到对方的声音。目前的交换机都支持全双工。 全双工的好处在于迟延小&…

全双工与半双工的区别

全双工传输英文写法是:Full&#xff0d;Duplex Transmissions 是指交换机在发送数据的同时也能够接收数据&#xff0c;两者同步进行&#xff0c;这好像我们平时打电话一样&#xff0c;说话的同时也能够听到对方的声音。目前的交换机都支持全双工。 全双工的好处在于迟延小&…

全双工、半双工和单工

一&#xff0c;单工、半双工和全双工的区别&#xff1a; 在串行通信中&#xff0c;数据通常是在两个终端&#xff08;如电脑和外设&#xff09;之间进行传送&#xff0c;根据数据流的传输方向可分为3种基本传送方式&#xff1a;单工、半双工和全双工。这3种传输方式的示意图和区…

单工,半双工和和全双工通讯的概念

单工&#xff1a;数据传输只支持数据在一个方向上传输&#xff1b; 半双工&#xff1a;允许数据在两个方向上传输&#xff0c;但某一时刻只允许数据在一个方向上传输&#xff0c;实际上是一种切换方向的单工通信&#xff0c;不需要独立的接收端和发送端&#xff0c;两者可合并…

单工、半双工和全双工的区别

--------------------------------------------- -- 时间&#xff1a;2018-11-06 -- 创建人&#xff1a;Ruo_Xiao -- 邮箱&#xff1a;xclsoftware163.com --------------------------------------------- 一、单工 1、数据只在一个方向上传输&#xff0c;不能实现双方通信。…

单工、双工、半双工、全双工通信常识

根据通信双方的分工和信号传输方向可将通信分为三种方式&#xff1a;单工、半双工与全双工。 单工通信&#xff0c;指通信双方设备中发送器与接收器分工明确&#xff0c;只能在由发送器向接收器的单一固定方向上传送数据&#xff0c;并且不能实现双向通信&#xff0c;例如&…

网络传输单工、半双工、全双工的解读

来源 在网络传输中&#xff0c;数据在线路上的传送方式可以分为单工通信、半双工通信和全双工通信三种。以下我们将通过简单的叙述方式&#xff0c;配图了解这三种方式的定义和区别。 一、单工 定义&#xff1a;单工数据传输只支持数据在一个方向上传输。 举例&#xff1a;意思…

单工通信、半双工通信和全双工通信的区别

对于点对点之间的通信&#xff0c;按照消息传送的方向与时间关系&#xff0c;通信方式可分为单工通信、半双工通信及全双工通信三种。 一、单工通信 单工通信&#xff08;Simplex Communication&#xff09;是指消息只能单方向传输的工作方式。 在单工通信中&#xff0c;通信…

【网络基础】半双工和全双工

讨论在网络中使用的两种不同的传输类型。 半双工&#xff08;half duplex&#xff09;和全双工&#xff08;full duplex&#xff09;。 引入基带传输、宽带传输两种传输频带&#xff0c;并讨论全双工的实现。 半双工 在半双工的网络中&#xff0c;两个设备使用网络电缆互连成一…

【数据存储】分布式文件系统SeaweedFS

2021.05.25 初始seaweedFS 安装 1、先去GitHub下载所需版本 linux_amd64.tar.gz2、然后放到服务器的文档中 /opt/software3、解压安装 tar -zxvf ./linux_amd64.tar.gz -C /opt/install/seaweed4、在seaweed安装包下安装seaweedfs文件夹及其下属两个文件夹 mkdir -p ./se…