Scala梳理

文章目录

变量

表达式

声明变量

语法:

var 变量名:类型 = 初始值

scala中的类型有:

Double Float Long Int Short Byte Unit Char Boolean yourClass

  • 所有的值类型都是Scala.*包下的
  • String类型延续了java.lang.String
  • 值类型的默认类型:Int(整数),Double(小数)
  • 类型支持支持自动推断
var a:Int = 0
var b:Double = 0.0
var c:String = ""
//简写(自动推断类型)

var d = 0
var e = 0.0
var f = ""

常量

只允许赋值一次

语法:val 常量名:类型= 值

小知识:

​ 创建对象时,建议使用常量val

val a = 10

//常量只允许赋值一次,如果继续进行赋值,会GG

类型转换

简单来说就是自动类型提升

Byte > Short > Int > Long > Float > Double

​ Char

小知识:

​ Char类型的值可以转换为Int类型

var a:Byte = 10
var b:Short = a
var c:Int  = b

//Char类型的值可以转换为Int类型
var q:Char = '96'
//可以用大类型指向大类型  byte >  int 但是不能反过来
var d:Long = 10
//这么玩会出事的
var e:Byte = d

运算符

Scala层次类型结构

在这里插入图片描述

  • Any是所有类的超类型,也就是顶级父类,顶级类型等。它定义了一些如:equals,toString方法等、Any有俩直接子类,AnyVal和AnyRef
  • AnyVal代表值类型。有9个预定义的非空的值类型分别是:DoubleFloatLongIntShortByteCharUnitBooleanUnit(类似于java中的void)是不带任何意义的值类型,它仅有一个实例可以像这样声明:()。所有的函数必须有返回,所以说有时候Unit也是有用的返回类型。
  • AnyRef代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef的子类型。如果Scala被应用在Java的运行环境中,AnyRef相当于java.lang.Object
  • Nothing是所有类型的子类型,也称为底部类型。没有一个值是Nothing类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。
  • Null是所有引用类型的子类型(即AnyRef的任意子类型)。它有一个单例值由关键字null所定义。Null主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。我们将在后面的章节中介绍null的替代方案。

小知识:

在scala的命令窗口,如果想写入多行代码,可以使用:paste模式,多行代码写入完成后,按ctrl+D键退出执行

分支语句

if语句

if(布尔表达式){
    //代码题
}

if … else

if(布尔条件){
    //为true执行
}else{
    //为false执行
}

if … else if …else语句

if(布尔条件1){
    // true 执行
}else if(布尔条件2){
    // true 执行
}else{
    // 以上条件不匹配 执行
}

总结:

  • scala中所有的表达式都是有值得,如果表达式的最后一行内容为打印语句,则表达式的值为() Unit类型
  • scala中代码块的最后一行内容为表达式的值
  • scala中代码块中有多行内容时,必须使用大括号{}包裹起来,如果只有一行内容,{}可以省略

匹配模式(相当于java中的switch)

小知识:

  • scala中并没有提供switch语句,但是提供了一个功能远超于switch语句的语法,叫做模式匹配

语法

变量名 match{
case 值 => 处理内容
case 值 => 处理内容
case _ => 处理内容
}

小知识:

  1. _ 类似于default
  2. 模式匹配不存在匹配穿透问题
var x:Int = 10

x match{
	case 5 => println("x的值为:" + x)
    case 10 => println("x的值为:" + x)
    case 15 => println("x的值为:" + x)
    case _ => println("你个铁憨憨,值没有一个是对的")
}

循环

while循环

语法:

while(循环条件){
// 循环体
}

var x = 10
while(x < 0){
    println(x)
    x -= 1
}

//小小小demo,1到100的和
var n = 1
var sum = 0
while(n <= 100){
    sum += n
    n += 1
}

do…while

语法:

do{
// 循环体
}while(循环条件)

// 1~100数值之和
var n = 1
var sum = 0
do{
    sum += n
    n += 1
}while(n <= 100)

for循环

语法:

for(i <- 0 to 100){
println(i)
}

var sum = 0
for(i <- 0 to 100){
    sum += i
}
println(sum)


var sum = 0
for(i <- 0 to 100){  // 数值区间Range(0~100),包含起始和结束数值
    sum += i
}
println(sum)

var sum = 0
for(i <- 0 until 100){  // 数值区间Range(0~99),包含起始和不包含结束数值
    sum += i
}
println(sum)


// 老生常谈的九九乘法表
for(m <- 1 to 9){
    for(n <- 1 to m){
        print(n+"*"+m+"="+(m*n) +"\t")
    }
    println()
}

嵌套for循环

嵌套for语法

多重循环体在for的()中用 ; 分割

// 原来的样子
for(m <- 1 to 9){
    for(n <- 1 to m){
        print(n+"*"+m+"="+(m*n) +"\t")
    }
    println()
}

//简化的样子
for(m <- 1 to 9;n <- 1 to m){
    if(m != n){
     	print(n+"*"+m+"="+(m*n) +"\t")   
    }eles{
        println(n+"*"+m+"="+(m*n) +"\t")
    }
}

迭代的步长

默认情况下for循环都是每次循环后加一

而通过 by 的设置,可以改变它的默认值

// 统计 1 ~ 100 偶数之和
var sum = 0
for( n <- 0 to 100 by 2){
    sum += n
}
println(sum) // 2550

if守卫

在一个1~~100的循环体中,我只想要其中的偶数值

那么,这个时候大多数想的是在循环体中加上if语句

对循环体进行限定,从而达到目标

(别问我为啥不用迭代步长,if用习惯了不行啊)

//正常写法
var sum = 0
for(n <- 1 to 100){
    if( n % 2 == 0){
        sum += n
    }
}

//身为程序员,当然要对自己好一点,能偷的懒绝对要偷
// if守卫简化实现  嘿嘿嘿
var sum = 0
for(n <- 1 to 10 if n % 2 != 0){
    sum += n
} 

//在来几个,练练手

// n 必须是奇数 并且n不能是5
var sum = 0
for(n <- 1 to 10 if n % 2 != 0 && n != 5){ 
    sum += n
}  // 20

// n 是奇数 或者n可以使6
var sum = 0
for(n <- 1 to 10 if n % 2 != 0 || n == 6){ 
    sum += n
}  // 31

break

小知识:

java中没有break和continue关键字

import scala.util.control.Breaks._

//所以要想在某些情况下停止循环,只能这么玩。
breakable {
  for (n <- 1 to 10) {
    if (n == 5) break()  // 在scala中如果是一个无参的方法,可以省却(),直接写方法名
    println(n)
  }
}

yield

yield就是相当于循环体的记事本,保存当前循环体遍历出了那些数据并且返回

也可以作出某些=处理后再返回

val newColle = for(n <- 1 to 10 if n % 2 == 1 && n != 5) yield n*3

函数

函数是一段具有特别功能的代码块

函数三要素:函数名,返回值,参数列表

public static 返回值类型 m1(参数列表){
// 函数体
return xxx;
}

函数声明

def 方法名(参数列表) : 返回值类型 = {
    方法体
}

//例子
def Text(x:Int,y:Int):Int ={
    x + y
}

Demo

package function

object Function01 {
  // main 快速生成main函数
  def main(args: Array[String]): Unit = {
    // 如何调用普通函数
   
  }

    def sum(x:Int,y:Int): Int = {
        //scala会被函数体中的最后一行代码当成返回值返回(前提返回值类型不是unit的祛疤膏将选)
        //或者是方法体的最后一行是一个无法返回的数据,比如一个打印语句
        x + y	
    }
    
    // 无返回值的函数  Unit类型的值为()
  	def sum1(x: Int, y: Int): Unit = {
    	println(x + y)
  	}
    
    //合理偷懒版
    
    //可以省略返回值类型,scala中支持自动类型推导,简单点来说						                       //就是这个鬼东西满聪明,可以算出你先返回的值类型,所以就不需要你自己写返回值类型
    def sum2(x: Int, y: Int){
        //println(x + y)
        x + y
    }

}

总结

  • 有返回值的函数,在声明时可以省略:返回值类型,scala自动根据类型推导返回值类型
  • 无返回值的函数,在声明时可以省略:返回值类型=, scala会自动认为是无返回值的函数

可变长参数

在java中如果声明可变长参数`public static void m1(String … args)

在scala中如果声明可变长参数def 函数名(args:Int*):返回值类型 ={}

object Function02 {

  def main(args: Array[String]): Unit = {
    println(m1(1, 2, 3, 4, 5)) // 15
    println(m1(1, 2, 3, 4, 5, 6, 7)) // 28
    println(m2(1, 1,2, 3, 4, 5, 6, 7)) // 29
  }

  // 声明一个Int类型的可变长参数 如:计算参数列表的整数之和
  def m1(args: Int*): Int = { // 数组
    var total = 0
    for (n <- args) {
      total += n
    }
    total
  }

  // 注意: 在定义可变长参数时,可变长参数需要放置在函数的参数列表之后
  def m2(init: Int, args: Int*) = {
    var default = init
    for (n <- args) {
      default += n
    }
    default
  }
}

参数默认值

scala的函数的参数是可以有默认值的

在使用时如果调用的时候传值,则使用传过来的值进行计算

如果调用时没有传值,这使用默认值

def sum(x: Int, y: Int, z: Int = 10): Int = { // 函数的参数有默认值的`参数名:类型名 = 默认值`
    x+y+z
}

带名参数

调用函数时,给指定的参数名的参数赋值

println(sum2(x = 1, z = 2)) // 13 【使用带名参数】
println(sum2(z = 2, x = 1)) // 13 【使用带名参数】 注意:带名参数使用时与函数的参数列表的顺序无关

def sum2(x: Int, y: Int = 10, z: Int): Int = { // 函数的参数有默认值的`参数名:类型名 = 默认值`
    x + y + z
}

Lazy值(懒加载)

在scala中,如果一个常量声明成了Lazy,则代表这个常量,会在第一次使用时才会被加载

变量不可以被声明为lazy值,不然报错,,,

初始化

object Function04 {
  def main(args: Array[String]): Unit = {
    // 语法 lazy 变量或者常量的声明

    // 立即进行初始化 ERROR
    val source = fromFile("D://abc.txt")

    // 懒加载的值
    lazy val source2 = fromFile("D://abc.txt")

    // 使用时,懒加载的值该初始化了,ERROR
    source2.mkString
    //mkString这个方法可以先简单的理解为读取文件类容的,并且可以跟根你指定的参数对文件进行划分
  }
}

递归函数

类似于java中的递归

个人理解,简单点来说,递归函数就是在函数体里面继续调用自己,向下执行,直到有值返回时在逐一向上

获取结果,最后到最上层返回结果给调用者

// 计算1~10的阶乘
package function

/**
  * 递归函数(求1~n阶乘)
  */
object Function05 {
  def main(args: Array[String]): Unit = {

    println(m1(3)) // 6
    println(m2(5))
  }

  // 求1~n的整数之和
  def m1(n: Int): Int = {
    var total = 0
    if (n > 0) {
      total += n + m1(n - 1)
    }
    total
  }

  // 第一次递归: n <= 0   total = 0
  // 第二次递归: n = 1    total = 0 + 1
  // 第三次递归: n = 2    total = 0 + 1 + 2
  // 第四次递归: n = 3   total = 0 + 1 + 2 + 3 = 6


  def m2(n: Int): Int = {
    var total = 1
    if (n >= 1) {
      total *= n * m2(n - 1)  // 1*2*3*4*5 = 120
    }
    total
  }

  // 第一次递归:
}

匿名函数

顾名思义:没有函数名的函数,就是匿名函数

语法:参数列表 => 函数体

(x:Int,y:Int) => x + y	//个人感觉scala牛逼就是牛逼到这在,写代码正儿八经的高效率

小知识:内置了23个匿名函数对象Function0~Function22, 匿名函数的背后对应的是一个函数对象

object Function{
     def main(args: Array[String]): Unit = {
         var functionTest:Function2[Int,Int,Int] = (i:Int,j:Int) => i + j
    	 println(functionTest(1,2))
         
         val list = List(1, 2, 3, 4, 5)
         //一个没有返回值的匿名函数
         var functionText2:Function[Int,Unit] = (i:Int) => println(i)
         list.foreach(functionText2)
     }
}

总结:

  • 定义匿名函数时要先确定当匿名函数的参数列表长度是多少
  • 知道匿名函数的参数数量后,可以通过Function0~~~Function22来限定参数的长度,并给参数设置泛型
  • 泛型的最后一个类型,是返回值的类型,最后一个泛型的类型,决定了当前匿名函数的返回值类型
  • 最后记得书写该匿名函数的算法(运算规则)

柯里化(currying)

定义:

柯里化函数指将接受多个参数的函数,改造为接受一个参数的函数,并且返回一个函数对象的过程

个人理解:以下是个人理解的柯里化函数的演算过程

//这一步没啥可说的,看第二部
def sum(x:Int,y:Int) = {
    x + y
}
//简化版
def sum2(x:Int) = {
    //这里做了使用了一个匿名函数,当sum2方法调用的时候,参数x被赋值,然后进入方法体
    //这时,发现匿名函数y没有值,运算无法完成,只能直接返回带一个参数的Function对象
    //返回时,这个方法变成了=>Function[Int,Int] = (y:Int) => 1 + y  这个样子
    //会等待用户给第二个参数的值,继续计算
    (y:Int) => x + y
}

//精简版
def sum3(x:Int)(y:Int) ={
    //简单通俗的理解 =>反正都要匿名函数来满足参数唯一的原则
    //那我何必不直接把那个匿名函数写到上面呢
    //也就是说  def sum3(x:Int)(y:Int) ={}  中(y:Int)不再是参数列表了,而是匿名函数
    //然后直接在  ={}  里面规定算法并且做返回(需要返回的话)
    //突然感觉scala语言设计的好巧妙有木有
    x + y
}
//----结果----------------------------------
println(sum(1,2))		//3
println(sum2(1))		//<function1>	之所以要先知给一个参数,就是想看看返回的是个什么玩意
println(sum2(1)(2))		//3      sum2方法的正确调用
println(sum3(1)(2))		//3

数组

>数组定义: 数据的集合,在内存中表示为一段连续的存储空间

数组的定义

// 语法:  val arr = Array[泛型](元素列表)
val arr:Array[Int] = Array[Int](1,2,3,4,5,6,7,8,9)//完整写法

val arr = Array[Int](1, 2, 3, 4, 5)//偷懒写法(别问为啥,问就是scala会自动类型推断)

数组的使用

和java没啥区别,都是用下标

//数组的声明
val arr = Array[Int](1, 2, 3, 4, 5)

//来吧,数组除了遍历,也没别的可玩了

  // 遍历方法1
    for(n <- 0 until arr.length){
      println(arr(n))  // 注意: 获取数组中元素使用的是 数组名(下标)
    }

 // 遍历方法2
    for(n <- arr){
      println(n)
    }

  // 遍历方法3
    for(n <- arr.indices){  // indices 获取到的是数组的下标数值区间
      println(arr(n))
    }


	//还有修改数组中的元素
    arr(3) = 10

数组排序

冒泡排序。。。

val arr = Array(5,10,2,1,6,7)

//额,模仿java版,,,
for(m <- 0 until arr.length){
      for(n <- 0 until arr.length - m -1){
        if(arr(n) > arr(n+1)){
          val tmp = arr(n)
          arr(n) = arr(n+1)
          arr(n+1) = tmp
         }
      }
 }

//scala【嵌套 + if守卫】
//scala中,双层for循环是可以写在一起的,if语句也是可以写在循环体中,语法就是这样的,,,
    for(m <- 0 until arr.length; n <- 0 until arr.length - m -1 if arr(n) > arr(n+1)){
        val tmp = arr(n)
        arr(n) = arr(n+1)
        arr(n+1) = tmp
    }

    for( m <- arr){
      println(m)
    }

可变数组ArrayBuffer

Array 定长数组,一旦声明 数据长度就固定不可变

ArrayBuffer 可变数组,类似于集合,数组会自动扩容

val ab = ArrayBuffer(1, 2, 3, 4, 5)
    // 可变数组添加元素
    ab += 6	// 相当于 => ab.+=(7)  这里的 += 是一个方法

    for(n <- ab){
      println(n)
    }

Array和ArrayBuffer相互转换

val ab = ArrayBuffer(1, 2, 3, 4, 5)

// arrayBuffer ---> array  可变到定长的转换
val arr = ab.toArray

// array ---> arrayBuffer 定长到可变长的转换
val abf = arr.toBuffer

多维数组

简单理解就是:

一维数组中每个元素就是一个单纯的值了,String,Int,yourClass等等类型的值了

二维数组则是每个元素里面还是一个数组,以此类推,就简称对维数组了

 val arr = Array.ofDim[Int](10, 4) // 类似于10行4列的二维表

    // 操作
    arr(1)(2) = 2

    // 获取
    println(arr(1)(2))

    for (m <- 0 to arr.length - 1) {
      for (n <- 0 to arr(m).length - 1) {
        print(arr(m)(n) + "\t")
      }
      println()
    }

类(class):对象的模板,成员(有什么)和方法(做什么)

简单类

语法:

语法: `class 类名{ // 成员或者方法 }

// 声明一个Animals
class Animals{}

// 创建对象
val animals:Animals = new Animals()
或
val animals:Animals = new Animals

小知识

  • 类中提供一个默认的无参的主构造器
  • 如果创建对象时调用是无参的构造方法,()可以省略

成员/方法

小知识

  • scala中成员全部都是私有的(private
  • ''scala中成员的getter方法名成员名()
  • scala中成员的setter方法名成员名_$eq(参数)
class ScalaTest{

  var id:Int = 0	//scala中,类的成员在被定义时,必须要有初始值
  var name:String = ""
  var password :String  = ""

  override def toString: String = "id:" + id + "\t name:" + name + "\t password" + password
}


val scalaTest = new ScalaTest
//声明常量成员,依然是私有的成员,scala自动为成员提供公开的getter【sex()】方法
//声明变量成员, 都是私有的成员,scala自动为成员提供公开的setter【id_$eq | name_$eq】/getter[id() | //name()]方法
    scalaTest.id = 1
    scalaTest.name = "zs"
    scalaTest.password = "123456"
    println(scalaTest)

方法

类中的方法和函数的语法一样

object ColorTest {
  def main(args: Array[String]): Unit = {
    val red: Red = new Red
    println(red.display())
    println(red)
  }
}

class Red {

  var name: String = "red"

  // 方法
  def display(): String = {
    this.name
  }

  override def toString: String ="红色"
}

手动生成get/set方法

语法:

  • getter方法 ----> def 区分成员名() = 成员值
  • setter方法 ----> def 区分成员名_(形参) {成员值 = 形参值}
object PersonTest {
  def main(args: Array[String]): Unit = {
    val person = new Person
    person.setName_("zs")

    println(person.getName)
  }
}

class Person {
  // 自定义name成员的getter和setter方法
  var name: String = ""

  // 自定义getter
  def getName(): String = this.name

  // 自定义setter方法
  def setName_(newName: String): Unit = this.name = newName
}

@BeanProperty => 手动生成get/set方法偷懒版

此注解加载成员的前面,scala编译时,会自动生产java版本和scala版本的getter/setter方法

object CourseTest {
  def main(args: Array[String]): Unit = {
    val chinese = new Chinese

    chinese.setCourseName("文言文")	//java风格的setter方法

    chinese.courseName = "四书五经" // scala风格setter方法
    val courseName = chinese.courseName
    println(courseName)
  }
}

class Chinese {

  @BeanProperty var courseName: String = "中文"
}

辅助构造器

辅助构造器,类似于java重载的构造方法

小知识:scala的一个类中,可以有一个主构造器和若干个辅助构造器

object FishTest {
  def main(args: Array[String]): Unit = {
    val fish1 = new Fish() // 调用无参数的主构造器
    val fish2 = new Fish("金鱼") // 调用无参数的主构造器
    val fish3 = new Fish("金鱼", "黄色") // 调用无参数的主构造器

    println(fish1)
    println(fish2)
    println(fish3)

    //----------------------------
    /*
    金鱼
    金鱼	黄色
    */
    //----------------------------
  }
}

// scala默认提供一个无参的主构造器
class Fish {

  var name: String = ""
  var color: String = ""

  // 辅助的构造器
  // 注意:辅助构造器必须以调用一个主构造器或者辅助构造器开始
  def this(name: String) {
    this()
    this.name = name
  }

  def this(name: String, color: String) {
    this(name)
    this.color = color
  }

  override def toString: String = this.name + "\t" + this.color
}

小知识

  • 辅助构造器,使用的关键字this
  • 辅助构造器内,第一行必须调用主构造器或者辅助构造器

主构造器

  • 声明,主构造器的参数列表直接写在类名之后:class Fish(var name:String,var color:String){}
  • 主构造器的方法体,是整个类,也就是类的{}的所有内容,注意主构造器方法体中的成员和方法仅仅是声明,并不会执行
object PandaTest {
  def main(args: Array[String]): Unit = {
    val p1 = new Panda("小黑","熊猫色")
    println()
    val p2 = new Panda("小黑","熊猫色")
  }
}

// 主构造器
class Panda(var name: String, var color: String) {

  var age: Int = 0

  // 方法体
  println("before")

  def sayHello(str: String): String = {
    str + name
  }
  println("after")
}

/*
//java的构造器
class Panda{
  	private String name;
  	private String color;
  	private Int age;
  	public Panda(String name,String color){
    	this.name = name;
    	this.color = color;
  	}
  }*/

对象(object)

代替java中static语法的,因为scala没有static,如果需要在scala的源文件中定义静态的成员和方法,使用object类型

单例一个对象

不管创建多少次,只存在一个对象实例

总结:

  • scala中一个类,类型为object,此类即为单例对象,j简单来说,就是对象(object)是单例的
  • 类体中成员和方法,都是静态成员【所有类共享的】
/**
  * scala中一个类,类型为object,此类即为单例对象
  */
object SingletonObject {

  // 静态属性
  var id: Int = 0

  // 静态方法
  def incrementId(): Int = {
    this.id = this.id +1
    this.id
  }

  def main(args: Array[String]): Unit = {
    val s1 = SingletonObject
    val s2 = SingletonObject

    val s3 = new SingletonObject()
    val s4 = new SingletonObject()

    println(s1 == s2) //true【单例】
    println(s3 == s4) //false【多例】

    val id1 = s1.incrementId()
    val id2 = s2.incrementId()
    println(id1+"\t"+id2)
  }
}

class SingletonObject {

}

伴生类/伴生对象

在一个scala的源文件中,class的名字和object名字一样,class类就被称为伴生类,object类就称为伴生对象

/**
  * 伴生类&伴生对象
  *
  * 注意:伴生类可以访问伴生对象的私有成员
  *
  *   java中: 普通方法能不能调用静态方法或者成员 【可以】
  */
class Person {

  def sayHi(): String = {
    "Hi:" + Person.name + "\t" + Person.id
  }
}

object Person {

  val id: Int = 0

  private val name: String = "zs"


  def main(args: Array[String]): Unit = {
    val person = new Person
    println(person.sayHi())
  }
}

apply & unapply

apply 方法是object类型中的一个方法,通常作为工厂方法创建伴生类的对象【简化方式】

结论:** 调用的伴生对象中的apply方法,用以创建伴生类**

class Cat(var name: String, var color: String) {

}

object Cat {

  // apply方法创建伴生类
  def apply(name: String, color: String): Cat = new Cat(name, color)

  def main(args: Array[String]): Unit = {
    // 创建Cat对象
    val cat = new Cat("小花", "white")

    // apply 工厂方法,用以创建类的对象,通常定义在伴生对象中
    // 创建伴生类对象时【简化写法】
    val cat2 = Cat("小黑","black")  // 等价于Cat.apply("小黑","black")
    val cat3 = Cat("小黑","black")  // 等价于Cat.apply("小黑","black")
    println(cat2 == cat3)
  }
}

unapply

unapply: 伴生对象中的方法,作用和apply方法相反,接受一个伴生类对象,返回伴生类对象的成员

class Cat(var name: String, var color: String) {

}

object Cat {

  // apply方法创建伴生类
  def apply(name: String, color: String): Cat = new Cat(name, color)

  // 将伴生类对象 拆箱为成员
  // Option 【避免出现空指针异常,有值 Some  无值 None】
  //   - None
  //   - Some
  def unapply(arg: Cat): Option[(String, String)] = {
    if (arg == null) None
    else {
      Some((arg.name, arg.color)) //(name,color) scala元组,可以存放任意类型的list集合 类型与java中的ArrayList[Object]
    }
  }

  def main(args: Array[String]): Unit = {
    // 创建Cat对象
    val cat = new Cat("小花", "white")
      
    // apply 工厂方法,用以创建类的对象,通常定义在伴生对象中
    // 创建伴生类对象时【简化写法】
    val cat2 = Cat("小黑", "black") // 等价于Cat.apply("小黑","black")
    val cat3 = Cat("小黑", "black") // 等价于Cat.apply("小黑","black")
    println(cat2 == cat3)

    // unapply方法应用
    val Cat((name, color)) = cat3   // Cat.unapply(cat3) = Some(cat3.name,cat3.color)
                                    // val name = cat3.name
                                    // val color = cat3.color
    println(name + "\t" + color)
    // ================================================
  }
}

继承

Java中的继承,子类继承父类等价于继承了父类成员和方法,使用的关键字extends,单继承

基本语法

class Animals {
  // 私有成员,公开的getter/setter方法,
  var name: String = ""

  def sayHi(): String = {
    "Hello:" + name
  }
}

class Dog extends Animals {
}

object Dog {
  def main(args: Array[String]): Unit = {
    val dog = new Dog
    dog.name = "小黑"  // 父类的setter
    println(dog.sayHi())  // 父类的sayHi
  }
}

小知识

  • scala的继承同样使用的是extends
  • scala的子类同样可以继承父类中的成员和方法

方法覆盖

覆盖:父类定义的方法无法子类需求时,在子类中定义相同的方法,在调用时,以子类优先

关键字override,不可省略

class Person {

  def run(): Unit = {
    println("人在奔跑!")
  }
}

class SmallStudent extends Person {

  // 父类方法的覆盖 `override`
  override def run(): Unit = println("小学生在学走路!")
}

object SmallStudent {
  def main(args: Array[String]): Unit = {
    val student = new SmallStudent
    student.run()  // 小学生在学走路!
  }
}

类型检查和转换

java中检查类型是:isInstanceof if(dog isInstanceof Animals) 这个样子

scala中是:

  • 判断类型是否兼容:isInstanceOf[类型]
  • 类型转换语法: asInstanceOf[另外一种类型]
val bool = student.isInstanceOf[Person]
println(bool) // true

val person = new Person
val bool2 = person.isInstanceOf[SmallStudent]
println(bool2)  // false

//-------------------------------------------
// 将小学生的类型 赋值父类型
val person2: Person = student.asInstanceOf[Person]

// 因为person2的真实类型是SmallStudent
val student2 = person2.asInstanceOf[SmallStudent]

用父类有参构造

/* 
	color父类没有无参的主构造器,只有一个有一个参数的主构造器
	scala中没有`Super()`语法,子类必须得调用父类的有参构造
 */
class Color(var name: String) {
  println("父类")
}


class Red(name: String) extends Color(name: String) {
  println("子类")
}

object Red {
  def main(args: Array[String]): Unit = {
    val red = new Red("红色")
    println(red.name)
  }
}


/*
//java中的父类有参构造调用

class Color{
  String name;
  public Color(String name){
    this.name = name;
  }
}

class Red extends Color{
  public Red(){
    super("red")
  }
}
*/

结论:

  • 如果调用父类的有参构造方法,需要在extends 父类(父类的有参构造)
  • 子类的主构造器中,必须有父类的成员,并且不需要声明为val或者var

重写字段

class A {
  var id: Int = 0
  val name: String = ""
  private var sex: Boolean = false // getter/setter私有的方法
}


class B extends A {
  // 子类覆盖父类成员id/name

  // 覆盖重写变量 id, 注意:省略了变量的关键字`var`和变量的类型
  id = 10

  // 覆盖重写常量 name
  override val name: String = "BB"

  private var sex: Boolean = true // 并不是继承,子类中特有的成员
}

object B {
  def apply(): B = new B()

  def main(args: Array[String]): Unit = {
    val b = B()
    println(b.id + "\t" + b.name + "\t" + b.sex)  //10	BB	true
  }
}

总结:

  • 如果子类需要覆盖父类中的变量(var),子类中不需要var关键字和变量的类型
  • 如果子类需要覆盖父类中的常量(val),子类必须在父类常量的基础之上加上override关键字
  • 如果父类中的私有成员(private),子类是无法覆盖,原因是:父类中属性和getter/setter方法都是私有的

抽象类

类似于java的抽象类,使用的关键字依然是abstract

  • 抽象类,可能有抽象成员和普通成员
  • 子类继承抽象类,必须得实现抽象类的声明方法,如果未实现,子类依然是一个抽象类
  • 抽象类只能声明引用而不能创建对象
  • 在实现抽象类的抽象方式或者属性时,依然使用的是关键字override
// 抽象类
abstract class Person2 {
  // 抽象字段
  val name: String

  // 抽象方法
  def playGame(): String
}

object Person2Test {
  def main(args: Array[String]): Unit = {
    //1. 抽象类只能声明引用而不能创建对象

    val person1:Person2 = new SmallStudent2
    println(person1.name +"\t" +person1.playGame)
    
    val person2:Person2 = new HighStudent2
    println(person2.name +"\t" +person2.playGame)
  }
}

class SmallStudent2 extends Person2 {

  override val name: String = "小学生"

  override def playGame(): String = "王者荣耀"
}

abstract class MiddleStudent2 extends Person2 {

  override def playGame(): String = "英雄联盟"
}

class HighStudent2 extends Person2 {

  override def playGame(): String = "绝地求生"

  override val name: String = "高中生"
}

匿名子类

和java中的匿名内部类差不多一样

abstract class A2 {
  def m1(): Unit
}

class B2 extends A2 {
  def m1(): Unit = {
    // 方法体
  }
}

// 匿名子类
val a2: A2 = new A2 {
    override def m1(): Unit = println("m1")
}
a2.m1()

包可见性

java访问修饰符: public ,default,protected,private

scala访问修饰符: default(public)、protected、private

在这里插入图片描述

特质(trait)

类似java中的接口

语法

  • 特质的关键字trait
  • 特质可以同时用于抽象方法和具体方法
  • 无父类 实现特质extends trait1 with trait2 with trait3 ...
  • 有父类 实现特质extends 父类 with 特质1 with 特质2 ...
/**
  * 等价于Java接口
  *   1. 公开静态常量
  *   2. 公开抽象方法
  *   3. JDK8之后,接口中也可以由具体的方法
  */
trait Person {

  // 特质中的属性
  var name: String

  // 特质中的一个抽象方法
  def run(): String
}

class Father

class SmallStudent extends Person { //无父类 实现特质extends trait1 with trait2 with trait3 ...
  override var name: String = "小学生"
  override def run(): String = name + "在走路!"
}

class MiddleStudent extends Father with Person{	//有父类 实现特质extends 父类 with 特质1 with 特质2 ...
  override var name: String = "中学生"
  override def run(): String = name +"在奔跑!"
}


object PersonTest {
  def main(args: Array[String]): Unit = {
    val student = new SmallStudent
    student.name = "小屁孩"
    println(student.run())
  }
}

带有具体实现的特质

scala中的特质是可以有具体实现方法的,有点类似有jdk1.8中的接口

package traites

trait Animals {
  // 注意:特质中既可以有抽象成员 也可以有具体成员
  var name: String = "小动物"

  def sleep(): Unit = {
    println(name + ":在睡觉!")
  }

  def eat(): String
}

class Dog extends Animals {
  override def eat(): String = "吃饭!"
  override def sleep(): Unit = println(name +":sleeping")
}

object Dog {
  def main(args: Array[String]): Unit = {
    val dog = new Dog
    dog.eat()
    dog.sleep()
    dog.name ="xx"
  }
}

小知识:让特质混有具体行为有一个弊端. 当特质改变时, 所有混入该特质的类都必须重新编译

带特质的对象 动态混入(mixin)重点

动态混入(mixin):指在创建单个对象时,可以在不破换对象声明的前提下,扩展对象的功能

在构造单个对象时, 你可以为它添加特质

  • 特质可以将对象原本没有的方法与字段加入对象中, 功能扩展
  • 无需修改类声明定义,扩展类的功能,灵活简便
  • 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
package traites.mixin

trait Animals {
  println("animals")
  def eat(): Unit
}

trait Person {
  println("person")
  def playGame(): Unit
}

trait Student extends Animals {
  println("student")
  def study(): Unit

  //覆盖父类中的eat方法
  override def eat(): Unit = println("学生吃的肉和菜")
}

/*//传统写法:在增强 SmallStudent 功能混入两个Trait
class SmallStudent extends Person with Student {
  override def playGame(): Unit = ???
  override def study(): Unit = ???
  override def eat(): Unit = ???
}*/

class SmallStudent { // 在不破坏类的声明要求下,扩展功能(吃、玩游戏、学习能力)
  println("smallStudent")
}

object ScalaTest {
  def main(args: Array[String]): Unit = {
    // 注意:特殊语法,可以在创建伴生类时混入特质[抽象类],增强伴生类的功能,这样的语法就称为动态混入(mixin)
    // 在有动态混入语法时:对象创建的顺序 从左到右进行初始化
    val smallStudent = new SmallStudent with Person with Student {
      override def playGame(): Unit = println("打游戏了")
      override def study(): Unit = println("学习使我快乐")
    }
    smallStudent.playGame()
    smallStudent.study()
    // 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
    smallStudent.eat() 	//因为Student特质中已经重写了eat方法,所以可以直接使用
  }
}

总结

  • 特殊语法,可以在创建伴生类时混入特质[抽象类],增强伴生类的功能,这样的语法就称为动态混入(mixin)
  • 在有动态混入语法时:对象创建的顺序 从左到右进行初始化
  • 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
  • 简单来说,动态混入就是在 new 一个类时,手抖的添加上相对于的特质,已完成某些需求,而不是在该类申明的时候加上特质,这样就保证了该类的纯净,不会有接口污染的情况出现

this别名

给当前类(this)起一个别名(可以随便定制,建议self

看scala的源码的话很发现很多源码开头都有一句:self => 这句相当于给this起了一个别名为selfself不是关键字,可以用除了this外的任何名字命名(除关键字)。就下面的代码,在Student内部,可以用this指代当前对象,也可以用self指代,两者是等价的。

class AA {
  self =>	//一般情况下,this就代表当前类,而现在self单标 AA类
  var id: Int = 0
  def getId(): Boolean = {
    //self.id => AA类
    this.id == self.id
  }
}

object AA {
  def main(args: Array[String]): Unit = {
    val aa = new AA
    val bool = aa.getId()
    println(bool)  //true
  }
}

//-------------------------------------------------------
class Outer {
  outer =>	//outer => Outer类
  val v1 = "here"

  class Inner {
    inner =>	//inner => Inner类
    val v1 = "-------------"
    println(outer.v1) // 用outer表示外部类,相当于Outer.this
    println(inner.v1)
  }
}
object Outer {
  def main(args: Array[String]): Unit = {
    val outer = new Outer()
    val inner = new outer.Inner

    /**
      * here
      * -------------
      */
  }
}

self-type(自动类型)

强制混入

trait Cat {
  val name: String
}

trait SmallCat{
  // 自类型:强制混入,表示当前特质类必须混入类型Cat
  self:Cat =>
  def sayHi():String = {
    self.name // 必须混入Cat
  }
}

class SmallFamilyCat extends SmallCat with Cat{
  override val name: String = "小花花"
}

object SmallFamilyCat{
  def main(args: Array[String]): Unit = {
    val cat = new SmallFamilyCat
    val result = cat.sayHi()
    println(result)
  }
}

高阶函数

匿名函数

上面写过,,,

传统写法:def sum(x:Int,y:Int):Int = x+y

简化写法:val f2:Function2[Int,Int,Int] = (x:Int,y:Int) => x+y

柯里化函数

上面也写过,,,

传统写法:def sum(x:Int) = (y:Int) => x+y

简化写法:def sum(x:Int)(y:Int) = x+y

函数对象

Scala默认提供了23个函数对象,Function0~Function22

object FunctionObject {
  def main(args: Array[String]): Unit = {
    val f2 = (x: Int, y: Int) => x + y

    /* // 创建Function2函数对象
        val f2:Function2[Int,Int,Int] = new Function2[Int, Int, Int] {
          override def apply(v1: Int, v2: Int): Int = v1 + v2
        }
     */

    val sum = f2(1, 2)
    println(sum)
  }
}

函数对象做为参数

小知识

​ Scala的函数对象可以作为值进行传递,原因函数也是对象

package function

object HighLevelFunction01 {
  def main(args: Array[String]): Unit = {
    val f2 = (x: Int, y: Int) => x + y
    sum(1,2,f2)
  }

  /**
    * 
    * @param x
    * @param y
    * @param f2 函数对象
    */
  def sum(x: Int, y: Int, f2: (Int, Int) => Int): Unit = {
    println(f2(x,y)) // 结果 3
  }
}

参数类型推断

老牛b的来了

package function

object HighLevelFunction02 {
  def main(args: Array[String]): Unit = {
    val arr = Array(1, 2, 3, 4, 5)

     arr.foreach((n: Int) => println(n))

    // 匿名函数 省略了参数列表的类型,因为匿名函数类型和数组元素类型一致
     arr.foreach((n) => println(n))

    // 接受单个参数 () 省略
     arr.foreach(n => println(n))

    // 接受单个参数 n => 省略
     arr.foreach(println(_))
      
    // 最终版 
     arr.foreach(println)
  }
}

一个关于高阶函数的小练习

object HighLevelFunction03ForWordCount {
  def main(args: Array[String]): Unit = {
    val lines = Array("Hello Hello","Hello Hadoop")

    lines
      .flatMap((line:String) => line.split(" "))  // 将一个内容展开为0~n个内容
      .map((word:String) => (word, 1))  // 将一个类型转换为另外一个类型
      .groupBy(t2 => t2._1)  // 根据元组中的word分组
      .map(t2 => (t2._1,t2._2.length)) 
      .foreach(println)
    
    println("-------------------------------")

    lines
      .flatMap(_.split(" "))
      .map((_,1))
      .groupBy(_._1)
      .map(t2 => (t2._1,t2._2.length))
      .filter(! _._1.equals("Hello"))  // 
      .foreach(println)
  }
}

函数闭包

闭包指在函数体内,可以访问相应作用域内的任何变量,因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。

我一直以为闭包这个概念只有JavaScript有,没想到scala也有,难道是都有s???

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

    var n = 0

    // 函数闭包: 函数对象 + 外部变量 = 闭包
    val f1 = (x: Int) => x + n

    println(f1(1))

    n = 100 // 函数对象闭包后,只保存一个外部变量的引用地址,当外部的变量在发生改变时,会影响到闭包后的结果

    println(f1(2))
  }
}

总结

  • 简单来说,就是 函数对象 + 外部变量 = 闭包
  • 其中外部变量可以影响该函数对象的结果

异常处理

scala中的异常处理,和java中的异常处理基本一致。

/**
  * 测试scala的异常处理
  */
object Exception01 {
  def main(args: Array[String]): Unit = {
    // m1()
    m2()
    println("-----------------------")
  }

  /**
    * 消极处理  throw
    *
    * 1) scala保留了java异常处理类
    * 2) scala不要通过throws声明可能抛出异常
    */
  def m1(): Unit = {
    // throw new RuntimeException
    throw new NullPointerException
    println("---------------")
  }

  /**
    * 积极处理 try ... catch...finally
    */
  def m2(): Unit = {
    try {
      var i: Int = 1 / 0
    } catch {
      // 模式匹配【详解】  匹配的try块中可能抛出异常的类型
      case e1: NullPointerException => println("空指针异常")
      case e2: RuntimeException => println("运行时异常")
      case e3: Exception => println("异常!!!")
      case _ => println("其它错误")
    }
    finally {
      println("finally block")
    }
  }
}

隐式类型转换

隐式转换函数(implicit conversion function)指的是以implicit关键字声明的带有单个参数的函数。这样的函数将被自动应用,将值从一种类型转换为另一种类型。隐式转换函数叫什么名字是无所谓的,因为通常不会由用户手动调用,而是由Scala进行调用。但是如果要使用隐式转换,则需要对隐式转换函数进行导入(import)。因此通常建议将隐式转换函数的名称命名为“one2one”的形式。

常用使用方式:

  • 隐式值
  • 隐式参数
  • 隐式函数
  • 隐式增强
  • 隐式类

Scala会考虑如下位置的隐式转换函数:

  • 位于源或目标类型的伴生对象中的隐式函数
  • 位于当前作用域可以以单个标识符指代的隐式函数

隐式转换在如下三种不同情况下会被考虑:【知道】

  • 当表达式类型与预期类型不同时【隐式值的使用】
  • 当对象访问一个不存在成员时【隐式增强】
  • 当对象调用某个方法,而这个方法的参数声明与传入参数不匹配时【隐式函数】

有三种情况编译器不会尝试使用隐式转换: 【知道】

  • 如果代码能够在不使用隐式转换的前提下通过编译,则不会使用隐式转换
  • 编译器不会尝试同时执行多个转换
  • 存在二义性的转换是错误

隐式值

在声明变量或常量添加implicit关键字

如:`implicit var name:String = “zs”

/**
  * 隐式值的使用
  */
object Implicit01 {
  def main(args: Array[String]): Unit = {
    // 1. 声明
    implicit var name:String = "zs"
    // implicit var address:String = "ls"  //error
    implicit val sex:Boolean = true  //ok


    // 2. 使用
    // 注意:
    //  1. 在使用隐式值时,通过隐式值的类型获取
    //  2. 同类型的隐式值只能有一个
    val newValue =implicitly[String]
    val newValue2 =implicitly[Boolean]
    println(newValue)
    println(newValue2)
  }
}

小知识:

  1. 在使用隐式值时,通过隐式值的类型获取
  2. 同类型的隐式值只能有一个

隐式参数

有一些特殊的方法,含有隐式参数

语法: def sayHi(implicit name:String):Unit = {}

小知识: 如果方法或者函数含有隐式参数,会自动在作用域寻找隐式值赋予隐式参数

object Implicit02 {
  def main(args: Array[String]): Unit = {
    implicit val name: String = "zs"
    implicit val default: Int = 0
    sayHi // 触发隐式参数的匹配
    sayHi("ls") // 因为调用时有具体的参数值,并不会触发隐式参数的匹配

    println(sum(1)(2))  // 3
    println(sum(1))  // 1
    println(sum2)  // 0 zs
    println(sum2(10,"ls"))  // 10 ls
  }

  /**
    *
    * @param name 隐式参数:在作用域中寻找同类型隐式值  同类型隐式值赋予隐式参数
    */
  def sayHi(implicit name: String): Unit = {
    println("Hello:" + name)
  }
 
  def sum(x: Int)(implicit y: Int): Int = {
    x + y
  }

  def sum2(implicit x: Int, y: String): String = { // x和y都为隐式参数
    x + "\t" + y
  }
}

隐式函数

将一种类型转换为另一种类型

package implicitconversion

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

    sayHi(new Person("zs")) // 正常写法
    sayHi("ls")
  }

  /**
    * 隐式函数: 将一种类型转换为另外一种类型
    * 注意:隐式函数不是我们调用的,是scala在编译不通过式,会在作用域寻找可以进行转换函数
    * @param
    */
  implicit def str2Person(str:String):Person={
    new Person(str)
  }

  def sayHi(p: Person): Unit = {
    println("Hello:" + p.name)
  }
}


class Person(var name: String) {
}

隐式增强

通过隐式转换增强某一个类的功能

/**
 * @Author: songbowen
 * @Description:
 * @Date: Create in 20:46 2019/12/9 
 */
object Test1 {
  def main(args: Array[String]): Unit = {

    val man = new Man("zs") //创建Man对象
    /**
     * 因为Man类里面没有fiy方法,只有superMam里面有
     * 1本来这里是会报错的,
     * 2但是这里有一个隐式函数,需要Mam对象当参数,返回值为SupMan(隐式函数scala会自动调用)
     * 3这时,我就通过man对象获取到了supMan对象,也就可以调用fiy了,也就不会报错了
     * 
     */
    man.fly
  }

  // man ---> superMan
  implicit def man2SuperMan(man:Man):SuperMan = {
    new SuperMan(man.name)
  }
  
}

class Man(var name: String){}

class SuperMan(var name: String) {
  def fly = {
    println("fly~~~~~~~")
  }
}

隐式类

implicit修饰的特殊类,注意不能独立声明,需要定义在一个类\对象的内部

/**
  * 隐式类
  * 这个和上面的差不多一样,我应该忘不了,忘了回来再翻翻代码。。。
  */
object Implicit05 {
  def main(args: Array[String]): Unit = {
    val man = new Man2("zs")
    man.fly

    // 隐式类: 主构造器接受一个类型 转换为隐式类类型  man2 ---> SuperMan2
    implicit class SuperMan2(man2: Man2) {
      var name: String = ""

      def fly = {
        this.name = man2.name
        println("fly~~~~~~~~")
      }
    }
  }
}

class Man2(var name:String)

隐式转换的导入

总结

  • 如果在当前作用域(源文件)有隐式转换,无需导入
  • 如果在当前作用域没有隐式转换,需要手动导入,使用语法import xxx
package implicitconversion

/**
  * 隐式转换的使用:
  *   - 对象中的隐式转换,使用如下的语法  对象名._
  *   - 类中隐式转换,使用如下的语法: val 引用 = new 类
  *                              import 引用._ 【语法】
  *
  * 注意:
  *   导入的隐式转换不能和当前类的成员产生命名冲突
  */
object Implicit06 {

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

    // 对象中的隐式转换,使用如下的语法
    import AA._
    val n2: Int = implicitly[Int]
    println(n2)
    val s: Boolean = implicitly[Boolean]
    println(s)

    // 类中隐式转换,使用如下的语法:
    val bb = new BB
    import BB._
    val str2: String = implicitly[String]
    println(str2)
    val c2: Char = implicitly[Char]
    println(c2)
  }
}

object AA {
  implicit val n: Int = 10
  implicit val sex: Boolean = false
}

class BB {
  implicit val str: String = "bb"

  implicit val c: Char = 'A'
}

泛型

规定了类或方法的使用类型

泛型类

语法:class[ 泛型类型 ] (主构造器) {

​ //代码块

}

-8/**
  * 泛型类
  * T S M K
  */
class Generic01[T, S, K](var name: T, var sex: S) {

  var age: K = _	// _相当于通配符,或者默认值

  override def toString: String = name + "\t" + sex + "\t" + age
}


object Generic01 {
  def main(args: Array[String]): Unit = {
    val g1 = new Generic01[String, Boolean, Int]("zs", false)
    g1.age = 10
    println(g1)
  }
}

泛型方法或函数

/**
  * 泛型方法或者函数
  *
  * 貌似也没啥可说的。。。
  * 泛型规定的什么类型,参数就用什么类型就好了
  */
object Generic02 {
  def main(args: Array[String]): Unit = {
    print[Int](Array(1, 2, 3, 4, 5))
    // print[Int](Array("a","b","c"))  //error
  }

  def print[T](arr: Array[T]): Unit = {
    arr.foreach(println)
  }
}

上边界

语法[T <: S] 限定T类型必须是S类型的子类,包含S类型本身

class A

class B extends A

// 上边界  T类型必须A子类型
class C[T <: A](var t: T)

class D

object C{
  def main(args: Array[String]): Unit = {
    val c1 = new C[A](new A)  //A类型本身 ok
    val c2 = new C[B](new B)  //A类型的子类B ok
    // val c3 = new C[D](new D)  //A类型的子类B ERROR
  }
}

下边界

语法:[T >: S] ,限定T类型必须S的父类型,包含S类型本身

package generic.lower

class A

class B extends A

// 下边界
class C[T >: B](var t: T)

class D extends B

object C {
  def main(args: Array[String]): Unit = {
    val c1 = new C[B](new B) // 下边界本身类型 ok
    val c2 = new C[A](new A) // A类型是B类型 ok
    val c3 = new C[D](new D) // D类型是B类型子类型 不符合下边界语义 ERROR
  }
}

视图边界

语法:[T <% S], 运行时尝试将T类型隐式转换为S类型

import java.text.SimpleDateFormat

/**
  * 视图边界【限定】
  *
  * str ---> java.util.Date
  */
class A[T <% java.util.Date](val date: T)

object A {
  def main(args: Array[String]): Unit = {
    val a = new A[String]("2019-11-20 15:21:30")
    println(a.date.getYear +"\t"+a.date.getMonth + "\t" +a.date.getDay)
  }

  // 提供一个隐式转换函数 str --> java.util.Date
 implicit def str2Date(str: String): java.util.Date = {
    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str)
  }
}

多重限定

多上界

语法:[T <: S with K] 表示T类型必须同时是S类型和K类型的子类型

package generic.multiupper

class A

class B extends A

class C extends B

// 多上界 表示:T 类型必须是B类型和A类型的子类型
// 特点:包含最小的上边界B类型、B类型和A类型的子类
class D[T <: B with A](t:T)

class E

object D{
  def main(args: Array[String]): Unit = {
    val d1 = new D[C](new C) // OK
    val d2= new D[B](new B) // OK
    // val d3= new D[A](new A) // ERROR
    // val d4= new D[E](new E) // ERROR
  }
}
多下界

语法:[T >: S with K] 表示T类型是S类型或者K类型的父类型

class A

class B extends A

class C

class D extends C

// 多下界 表示 T类型必须是B类型或者D类型的父类型
class E[T >: B with D](t: T)

// class F
class F extends D

object E{
  def main(args: Array[String]): Unit = {
    val e1 = new E[A](new A) //ok
    val e2 = new E[D](new D) //ok
    val e3 = new E[B](new B) //ok
    val e4 = new E[C](new C) //ok
    //val e5 = new E[F](new F) //error
  }
}
既有上界,又有下界

语法:[T >: S <: K] 表示T类型的上边界为K类型,下边界为S类型

package generic.upper_lower

class A

class B extends A

class C extends B

// 既有上边界 又下边界
class D[T >: C <: A](var t: T)

class E extends C

object D{
  def main(args: Array[String]): Unit = {
    val d1 = new D[B](new B)  //ok
    val d2 = new D[A](new A)  //ok
    val d3 = new D[C](new C)  //ok
    // val d4 = new D[E](new E)  //error
  }
}

协变

语法:[+T]

​ 如果T类型是T1类型的父类型,则 C[T]类型是C[T1]类型的父类

class A

class B extends A

class C[+T]

C[A] ---> C[B] 父类

//---------------------------------------------------------
class A

class B extends A

class C[+T]

object C {
  def main(args: Array[String]): Unit = {
    val c1 = new C[B]
    // 协变后 C[A] 依然是 C[B] 父类型
    val c2: C[A] = c1
  }
}

逆变

语法:[-T]

如果T类型是T1类型的父类型,则 C[T1]类型是C[T]类型的父类

class A

class B extends A

class C[-T]

C[B] ---> C[A] 父类
//-------------------------------------------------------
package generic.nb

class A

class B extends A

// 逆变
class C[-T]

object C{
  def main(args: Array[String]): Unit = {
    val c1 = new C[A]
    
    val c2:C[B] = c1
  }
}

不变

语法:[T] ,使用方法同泛型类和泛型方法 (这有点扯犊子,,,)

上下文限定

语法:[T:M] 上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的隐式值。

// 语法:`[T:M]`
//
//    上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的隐式值。
class Person(var name: String, var age: Int)

/**
  * T: 泛型类(Ordering)
  *
  * @tparam T
  */
class CompareUtils[T: Ordering](p1: T, p2: T) {

  def compare: Int = {
    val ordering = implicitly[Ordering[T]] // 获取一个Ordering[T]隐式值
    ordering.compare(p1,p2)
  }
}

object Person {
  // 声明ordering[Person]的隐式值【对象】
  implicit val ordering: Ordering[Person] = new Ordering[Person] {
    override def compare(x: Person, y: Person): Int = if (x.age > y.age) 1 else -1
  }

  def main(args: Array[String]): Unit = {
    val p1 = new Person("zs", 20)
    val p2 = new Person("ls", 30)
    val tools = new CompareUtils[Person](p1, p2)
    if(tools.compare == 1) println("大于")
    else println("小于")
  }
}

集合

Scala提供了一套很好的集合实现,提供了一些集合类型的抽象。

Scala 集合分为可变的和不可变的集合:

  • 可变集合(mutable) 可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。
  • 不可变集合(默认 immutable)类永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。

顺带提一下java中的集合。。。

Java中集合结构:

|- Iterable

​ |- Collection

​ |- List 有序 有小标 元素可重复

​ |- ArrayList

​ |- Vector

​ |- LinkedList

​ |- Set 无序 无下标 元素不可重复

​ |- HashSet

​ |- TreeSet

​ |- LinkedHashSet

​ |- Map K-V键值对

​ |- HashMap

​ |- HashTable

​ |- TreeMap

​ |- Properties

seq序列

Range

数值范围区间

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

    println(1 to 10)	// => new Range(1,2,3,4,5,6,7,8,9,10)

    println(new Range(1,10,2))	//(启始值,结束值,步长) => new Range(1,3,5,7,9)

  }
}
//-----------------结果--------------------------------------
Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Range(1, 3, 5, 7, 9)

Process finished with exit code 0

Vector(向量集合)

树形结构实现,查找效率高

println(Vector[Int](1,2,3,4,5,6))
println(Vector(1,2,3,4,5,6)) // =>自动类型推断
println(Vector("aa","bb","cc","dd"))
/************结果************************
        Vector(1, 2, 3, 4, 5, 6)
        Vector(1, 2, 3, 4, 5, 6)
        Vector(aa, bb, cc, dd)
List (不可变的列表)

immutable 不可变的列表

 	// 声明
    val list = List[Int](1,2,3,4,5,6,7)
    println(list)
    println(list(2))  //通果下标获取元素
    //list(2) = 10  不可以对list集合中的元素进行更改

    //list集合是不能更改的,要想添加一个元素,会返回一个新的list集合,
    //原list集合元素不变
    var list2 = list :+ 8 //添加元素
    println(list2)

    // 将0添加到集合的头部
    val list3 = 0 +: list
    println(list3)

    // 移除元素
    val list5 = list.drop(1)
    println(list5)

Nil

空列表,没有任何元素的list集合

val list = Nil
::&:::

:: 如:A :: List 将A作为整体添加到List集合的头部

::: 如:A ::: List` 将A中的各个元素添加到List集合的头部

//感觉这些基础的语法在黑窗口里面敲比较尊重它

scala> 1 :: Nil
res20: List[Int] = List(1)

scala> 2:: res20
res21: List[Int] = List(2, 1)

scala> 3::res21
res22: List[Int] = List(3, 2, 1)

scala> List(1,2,3) :: res22
res23: List[Any] = List(List(1, 2, 3), 3, 2, 1)

scala> List(1,2,3) ::: res22
res24: List[Int] = List(1, 2, 3, 3, 2, 1)
ListBuffer(可变列表)

mutable 可以在原始集合的基础之上,进行改操作

// 声明一
scala> scala.collection.mutable.ListBuffer(1,2,3,4,5)
res29: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4, 5)

// 声明二
scala> import scala.collection.mutable._  // 等价于  import scala.collection.mutable.*
scala> ListBuffer("A","B")
res30: scala.collection.mutable.ListBuffer[String] = ListBuffer(A, B)

// 使用
// 获取
scala> res1(1)
res2: String = B
// 修改
scala> res1(1) = "C"
scala> res1
res4: scala.collection.mutable.ListBuffer[String] = ListBuffer(A, C)

// 相同的对象
scala> res4 == res1
res5: Boolean = true

// 添加
scala> res1 += "D"
res6: res1.type = ListBuffer(A, C, D)

scala> res1.+=("E")
res7: res1.type = ListBuffer(A, C, D, E)

// 移除
scala> res1 -= "A"
res9: res1.type = ListBuffer(C, D, E)

// ++= 将后一个集合中的元素追加到当前集合的末尾
scala> res1 ++= ListBuffer("X","Y","Z")
res13: res1.type = ListBuffer(D, E, C, X, Y, Z)

// A +=:ListBuffer  表示将A添加到ListBuffer头部
scala> "o" +=: res1
res14: res1.type = ListBuffer(o, D, E, C, X, Y, Z)

结论

  • 通常情况下不可变集合的操作符是不含=,操作完成后返回一个新集合
  • 通常情况下可变集合的操作符是含有=,操作时在当前集合完成的,不会返回新集合
遍历

没啥说的,上代码

scala> res1.foreach( println(_))  // “_“   代表的是集合中的每一个元素
o
D
E
C
X
Y
Z

scala> for(n <- res1){println(n)}
o
D
E
C
X
Y
Z

scala> for(n <- 0 until res1.size){println(res1(n))}
o
D
E
C
X
Y
Z

set

set不可变(immutable)
// 声明
scala> scala.collection.immutable.Set[Int](1,2,3,2)
res24: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

// 获取
scala> res24.foreach(println)
1
2
3

// 改操作
scala> res24 + 4
res26: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)

scala> res24 + 3
res27: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

scala> res24 == res26
res28: Boolean = false

scala> res24 - 2
res29: scala.collection.immutable.Set[Int] = Set(1, 3)
Set可变集合(mutable)
scala> scala.collection.mutable.Set[Int](1,2,3,3)
res31: scala.collection.mutable.Set[Int] = Set(1, 2, 3)

// 操作
scala> res31 += 4
res32: res31.type = Set(1, 2, 3, 4)

scala> res32 == res31
res33: Boolean = true

scala> res31 -= 2
res34: res31.type = Set(1, 3, 4)
// 遍历方法(略)

HastSet(略)

TreeSet(略)

这俩基本用不上,,,

Map

元素 k,v键值对

可变和不可变

// 方法一
// 可变 
scala> Map("bj"->"北京","tj"->"天津")
res35: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> 北京)

// 不可变
scala> scala.collection.immutable.Map[String,String]("bj"->"北京","tj"->"天津")
res36: scala.collection.immutable.Map[String,String] = Map(bj -> 北京, tj -> 天津)

// 方法二
scala> Map(("bj","北京"),("tj","天津"))
res37: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> 北京)

scala.collection.immutable.Map(("bj","北京"),("tj","天津"))


// 操作
// 根据k取V值
scala> res0("bj")
res1: String = 北京

// 根据k修改v(可变集合)
scala> res3("bj")="bjj"

scala> res3
res5: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> bjj)

//添加k v对
scala> res3.+=(("sh","上海"))
res6: res3.type = Map(tj -> 天津, bj -> bjj, sh -> 上海)

//根据k移除kv
scala> res3.remove("tj")
res7: Option[String] = Some(天津)

scala> res3
res8: scala.collection.mutable.Map[String,String] = Map(bj -> bjj, sh -> 上海)

// 通过声明Tuple1元组 删除kv
scala> res3 -= (("sh"))
res9: res3.type = Map(bj -> bjj)

scala> res3
res10: scala.collection.mutable.Map[String,String] = Map(bj -> bjj)

// 遍历
scala> res3.foreach(t => println(t._1 +"\t"+t._2))
bj      bjj

// 键遍历
scala> res13.foreach(k =>println(res3(k)))
bjj

// 值遍历
scala> res3.values
res15: Iterable[String] = HashMap(bjj)

Tuple 元组

一种特殊的数据类型,可以存放任意的值,类似于ArrayList[Object]

scala中内置了22个元组Tupe1~Tuple22

通常使用Tuple2,描述Map集合的KV键值对

// 声明
scala> ("zs",1)
res18: (String, Int) = (zs,1)

scala> res18.getClass
res19: Class[_ <: (String, Int)] = class scala.Tuple2

scala> ("zs",1,false)
res20: (String, Int, Boolean) = (zs,1,false)

scala> res20.getClass
res21: Class[_ <: (String, Int, Boolean)] = class scala.Tuple3

// 获取元组中的值  语法: 元组引用名._n  获取元组中第n个值
scala> res20._1
res22: String = zs

scala> res20._2
res23: Int = 1

// 元组的遍历
scala> res20.productIterator
res24: Iterator[Any] = non-empty iterator

scala> for(n <- res24) println(n)
zs
1
false

Java和Scala集合的互操作

object JavaAndScalaCollectionOpt {
  def main(args: Array[String]): Unit = {
    // 导入隐式转换
    import scala.collection.JavaConverters._

    // 1. 创建Java集合
    val list = new java.util.ArrayList[Int]()
    list.add(1)
    list.add(2)
    list.add(3)

    // 2. java集合转化scala的集合
    val scalaList = list.asScala
    val newScalaList = scalaList :+ 4
    newScalaList.foreach(println)

    // 3. scala集合转换java集合
    val converterJavaList = newScalaList.asJava

    println(converterJavaList.get(3))
  }
}

模式匹配和样例类

match pattern

模式匹配是Scala中非常有特色,非常强大的一种功能。模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理。

但是Scala的模式匹配的功能比Java的swich case语法的功能要强大地多,Java的swich case语法只能对值进行匹配。但是Scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配、对Array和List的元素情况进行匹配、对case class进行匹配、甚至对有值或没值(Option)进行匹配。

值匹配

/**
  * 值匹配
  */
object MatchPattern01 {
  def main(args: Array[String]): Unit = {

    val str:String = "C"

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    str match{
      case "A" => println("A")
      case "B" => println("B")
      case _ => println("其它")  // `_` 表示default 其它
    }
  }
}

if守卫

不是for循环里面的if守卫啊,虽然区别不大

/**
  * 值匹配
  */
object MatchPattern02 {
  def main(args: Array[String]): Unit = {

    val age:Int = 45  // 类似于: if... else if... else

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    // if守卫
    age match{
      case _ if(age > 60) => println("老年人")  // `_` 匹配任意值
      case _ if(age > 40) => println("中年人")
      case _ if(age >= 20) => println("青年人")
      case _ => println("少年&儿童")  // `_` 表示default 其它
    }
  }
}

模式匹配中的变量

package matchpattern

/**
  * 变量
  */
object MatchPattern03 {
  def main(args: Array[String]): Unit = {
      
    val age: Int = 18 // 类似于: if... else if... else

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    // if守卫
    age match {
      case a1 if (a1 > 60) => println("老年人:" + a1) // `_` 匹配任意值
      case a2 if (a2 > 40) => println("中年人:" + a2)
      case a3 if (a3 >= 20) => println("青年人:" + a3)
      case _default => println("少年&儿童:" + _default) // `_` 表示default 其它
    }
  }
}

类型匹配

对表达式的类型进行匹配,Scala推荐使用这样的方式进行类型匹配,而不是使用isInstanceOf操作

package matchpattern

/**
  * 类型匹配
  */
object MatchPattern04 {
  def main(args: Array[String]): Unit = {
    val t: Any = "aa"

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    // if守卫
    t match {
      case d: Double => println("小数:" + d)
      case n: Int => println("整数:" + n)
      case b: Boolean => println("布尔:" + b)
      case _o => println("其它类型:" + _o)
    }
  }
}

集合元素匹配

个数,类型,类容

package matchpattern

/**
  * 集合匹配
  */
object MatchPattern05 {
  def main(args: Array[String]): Unit = {
   
    val list = List[Int](1,2,3)

    // scala编译器 编译完成后会抹掉集合类型的泛型信息,这样的话在运行时是无泛型的一种匹配,谁在最前面谁优先被执行
    list match {
      case l1:List[String] => println("list[String]")
      case l2:List[Int] => println("list[Int]")
      case _ => println("other")
    }
  }
}
al list = new java.util.ArrayList[Int]()
    list.add(1)
    list.add(2)
    list.add(3)

    // 2. java集合转化scala的集合
    val scalaList = list.asScala
    val newScalaList = scalaList :+ 4
    newScalaList.foreach(println)

    // 3. scala集合转换java集合
    val converterJavaList = newScalaList.asJava

    println(converterJavaList.get(3))
  }
}

模式匹配和样例类

match pattern

模式匹配是Scala中非常有特色,非常强大的一种功能。模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理。

但是Scala的模式匹配的功能比Java的swich case语法的功能要强大地多,Java的swich case语法只能对值进行匹配。但是Scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配、对Array和List的元素情况进行匹配、对case class进行匹配、甚至对有值或没值(Option)进行匹配。

值匹配

/**
  * 值匹配
  */
object MatchPattern01 {
  def main(args: Array[String]): Unit = {

    val str:String = "C"

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    str match{
      case "A" => println("A")
      case "B" => println("B")
      case _ => println("其它")  // `_` 表示default 其它
    }
  }
}

if守卫

不是for循环里面的if守卫啊,虽然区别不大

/**
  * 值匹配
  */
object MatchPattern02 {
  def main(args: Array[String]): Unit = {

    val age:Int = 45  // 类似于: if... else if... else

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    // if守卫
    age match{
      case _ if(age > 60) => println("老年人")  // `_` 匹配任意值
      case _ if(age > 40) => println("中年人")
      case _ if(age >= 20) => println("青年人")
      case _ => println("少年&儿童")  // `_` 表示default 其它
    }
  }
}

模式匹配中的变量

package matchpattern

/**
  * 变量
  */
object MatchPattern03 {
  def main(args: Array[String]): Unit = {
      
    val age: Int = 18 // 类似于: if... else if... else

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    // if守卫
    age match {
      case a1 if (a1 > 60) => println("老年人:" + a1) // `_` 匹配任意值
      case a2 if (a2 > 40) => println("中年人:" + a2)
      case a3 if (a3 >= 20) => println("青年人:" + a3)
      case _default => println("少年&儿童:" + _default) // `_` 表示default 其它
    }
  }
}

类型匹配

对表达式的类型进行匹配,Scala推荐使用这样的方式进行类型匹配,而不是使用isInstanceOf操作

package matchpattern

/**
  * 类型匹配
  */
object MatchPattern04 {
  def main(args: Array[String]): Unit = {
    val t: Any = "aa"

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    // if守卫
    t match {
      case d: Double => println("小数:" + d)
      case n: Int => println("整数:" + n)
      case b: Boolean => println("布尔:" + b)
      case _o => println("其它类型:" + _o)
    }
  }
}

集合元素匹配

个数,类型,类容

package matchpattern

/**
  * 集合匹配
  */
object MatchPattern05 {
  def main(args: Array[String]): Unit = {
   
    val list = List[Int](1,2,3)

    // scala编译器 编译完成后会抹掉集合类型的泛型信息,这样的话在运行时是无泛型的一种匹配,谁在最前面谁优先被执行
    list match {
      case l1:List[String] => println("list[String]")
      case l2:List[Int] => println("list[Int]")
      case _ => println("other")
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值