Scala

数据类型:

Unit 表示无值,和其它语言中void相同

Null 空值或空引用

Nothing 所有类型的子类型,表示没有值

Any 所有其它类型的超类,任何实例都是Any类型

AnyRef 所有引用类型的超类

AnyVal  所有值类型的超类

var和val的区别:var是可变量,val是不可变量(相当于java中的final)。

val支持推导类型,即根据变量的初始值推导出变量的类型,但var必须显式声明

val线程安全,因为它不可变,不会引起结果的差异和竞态条件。

val更适合函数式编程

竞态条件(Race Condition)是指在多线程编程中,多个线程对共享资源的访问和操作没有正确同步导致的不确定结果。竞态条件可能会导致程序出现意外的行为,产生不一致的结果。

竞态条件发生的原因是多个线程并发执行时,它们对共享资源的访问没有进行适当的同步操作。这意味着多个线程可以以任意的顺序读取和写入共享资源,导致操作结果的不可预测性。

以下是一些常见的竞态条件情况:

  1. 读-修改-写(Read-Modify-Write)操作:多个线程同时读取同一个变量的值,并根据这个值进行修改后再写回。如果多个线程同时执行读取和写入操作,可能会导致最终结果不正确。

  2. 检查-执行(Check-Then-Act)操作:多个线程同时检查某个条件,并根据条件执行相应的操作。如果多个线程同时执行检查操作,可能导致条件的结果在执行操作之前被修改,从而产生错误的行为。

  3. 非原子操作:多个线程同时执行多个操作,这些操作需要以原子方式执行才能保证正确性。如果没有适当的同步措施,可能导致操作之间的交叉执行,产生不一致的结果。

为了避免竞态条件,需要使用适当的同步机制来保护共享资源的访问,例如使用锁(Lock)、互斥量(Mutex)、信号量(Semaphore)等。这些同步机制可以确保多个线程按照正确的顺序访问共享资源,避免竞态条件的发生。

在Java和Scala中,数值类型之间的转换关系如下:

  1. 从较小的类型向较大的类型转换是自动进行的,称为隐式类型转换。这是因为较小的类型可以容纳较大类型的值,不会导致数据丢失或溢出。
  • Byte 可以自动转换为 ShortIntLongFloatDouble
  • Short 可以自动转换为 IntLongFloatDouble
  • Char 可以自动转换为 IntLongFloatDouble
  • Int 可以自动转换为 LongFloatDouble
  • Long 可以自动转换为 FloatDouble
  • Float 可以自动转换为 Double
  1. 从较大的类型向较小的类型转换需要显式进行,称为显式类型转换或强制类型转换。这是因为较大的类型可能无法完全容纳较小类型的值,可能会导致数据丢失或溢出。
  • Double 需要显式转换为 FloatLongIntShortByte
  • Float 需要显式转换为 LongIntShortByte
  • Long 需要显式转换为 IntShortByte
  • Int 需要显式转换为 ShortByte
  • Short 需要显式转换为 Byte
val intValue: Int = 1000
val byteValue: Byte = intValue.toByte

val doubleValue: Double = 3.14
val intValue: Int = doubleValue.toInt

隐式转换案例

通过定义一个隐式转换函数 intToString,它接受一个 Int 类型的参数,并将其转换为 String 类型。然后,在主函数中将 intValue 赋值给 stringValue 变量时,由于没有直接的类型匹配,编译器会自动应用隐式类型转换函数将 intValue 隐式转换为 String 类型。

// 定义一个隐式转换函数
implicit def intToString(value: Int): String = value.toString

def main(args: Array[String]): Unit = {
  val intValue: Int = 42

  // 隐式类型转换:将 intValue 转换为 String 类型
  val stringValue: String = intValue

  println(stringValue)
}

if-else举例

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

  if (x > 5) {
    println("x is greater than 5")
  } else {
    println("x is less than or equal to 5")
  }
}

for循环

for(i <- 1 to 9;j <- 1 until 10){

}

scala中没有break和continue的实现,但可以通过breakable关键字和递归函数替代实现。

使用breakable{}代码块包裹需要使用break功能的代码块

import scala.util.control.Breaks._

var i = 0
breakable {
  while (i < 10) {
    if (i == 5) break // 使用 breakable 块中的 break 方法实现中断循环
    println(i)
    i += 1
  }
}

递归

def printNumbers(n: Int): Unit = {
  if (n < 1) return // 递归终止条件
  if (n == 5) return // 使用 return 语句实现跳过当前迭代

  println(n)
  printNumbers(n - 1) // 递归调用
}

printNumbers(10)

方法的定义:Method

e4b85e211aca4b6cadac9b78b7b42d84.png

def fun(x int ,y int) :unit = {

}

def fun1 (a : Int , b : Int)= a+b

  • 方法可以写返回值的类型也可以不写,会自动推断,有时候不能省略,必须写,比如在递归方法中或者方法的返回值是函数类型的时候。
  • scala中方法有返回值时,可以写return,也可以不写return,会把方法中最后一行当做结果返回。当写return时,必须要写方法的返回值。
  • 如果返回值可以一行搞定,可以将{}省略不写
  • 传递给方法的参数可以在方法中使用,并且scala规定方法的传过来的参数为val的,不是var的。
  • 如果去掉方法体前面的等号,那么这个方法返回类型必定是Unit的。这种说法无论方法体里面什么逻辑都成立,scala可以把任意类型转换为Unit.假设,里面的逻辑最后返回了一个string,那么这个返回值会被转换成Unit,并且值会被丢弃。

函数Function

定义一个函数

val  f1=(x:Int,y:Int)=>x+y

调用: f1(1,2)

匿名函数

(x:Int,y:Int)=>x+y

函数和方法的区别:函数式独立的、可单独调用的代码块,方法是类对象中的一个成员,通过类的实例进行调用

函数式编程:

高阶函数:返回值是函数,或参数是函数,或同时都是的函数

返回值是函数的函数:

def f(a:int,b:int):(int,int)=>int ={
    def f2(x:int,y:int):int={
         x+y+a+b
    
    }
    f2

}

println(f(1,2)(3,4))


 //函数的返回是函数
    //1,2,3,4相加
    def hightFun2(a : Int,b:Int) : (Int,Int)=>Int = {
      def f2 (v1: Int,v2:Int) :Int = {
        v1+v2+a+b
      }
      f2
    }
    println(hightFun2(1,2)(3,4))

这段代码定义了一个高阶函数 hightFun2,它接受两个整型参数 ab,并返回一个函数 (Int, Int) => Int

函数 hightFun2 内部定义了一个局部函数 f2,它接受两个整型参数 v1v2,并返回它们的和再加上外部参数 ab 的值。这个局部函数 f2 捕获了外部函数 hightFun2 的参数 ab,因此在函数体内部可以直接访问这两个参数。

最后,函数 hightFun2 返回了局部函数 f2。在打印语句中,我们通过 hightFun2(1, 2) 调用了 hightFun2 函数,并将返回的函数 (Int, Int) => Int 作为参数传递给了另一个函数调用 println。然后,我们通过 (3, 4) 作为参数调用了这个返回的函数,得到结果 1 + 2 + 3 + 4 = 10,并将结果打印出来。

因此,整体来说,这段代码的含义是定义了一个接受两个参数的高阶函数 hightFun2,它返回一个函数,这个返回的函数接受两个参数,并返回这两个参数的和再加上外部传入的参数的值。然后,通过调用 hightFun2(1, 2)(3, 4),得到了最终的结果并打印出来。

 def hightFun3(f : (Int ,Int) => Int) : (Int,Int) => Int = {
      f
    } 
    println(hightFun3(f)(100,200))
    println(hightFun3((a,b) =>{a+b})(200,200))

 //以上这句话还可以写成这样
    //如果函数的参数在方法体中只使用了一次 那么可以写成_表示
    println(hightFun3(_+_)(200,200))

这段代码定义了一个高阶函数 hightFun3,它接受一个函数 f,该函数的类型为 (Int, Int) => Int,并返回一个函数 (Int, Int) => Int

在第一个打印语句中,我们调用了 hightFun3(f),将函数 f 作为参数传递给 hightFun3,然后将返回的函数再次调用,并传入参数 (100, 200)。由于函数 hightFun3 的实现中直接返回了参数函数 f,因此第一个打印语句的结果就是调用函数 f,即 f(100, 200)

在第二个打印语句中,我们调用了 hightFun3,并传入了一个匿名函数 (a, b) => { a + b }。这个匿名函数接受两个参数,并返回它们的和。因此,第二个打印语句的结果就是调用这个匿名函数,即 (a, b) => { a + b }(200, 200),最终结果为 400

注意点:{}中最后一行为默认返回值

匿名函数 中的参数如果只使用一次,可以 简写,即  (a,b)=>{a+b} 简化为 (_+_)

柯里化函数: 高阶函数调用的简化

将原来需要接受多个参数的函数转换成只要一个参数的函数过程,并且返回 一个函数(参数为 剩余的参数)。

  scala柯里化风格的使用可以简化主函数的复杂度,提高主函数的自闭性,提高功能上的可扩张性、灵活性。可以编写出更加抽象,功能化和高效的函数式代码。
//柯理化
object KLH {
  def main(args: Array[String]): Unit = {
    def klh(x:Int)(y:Int) =x*y
    val res=klh(3)(_)
   println(res(4))
// KLH(3)(_)(4)
  }
}
/**
     * 柯里化函数
     */
    def fun7(a :Int,b:Int)(c:Int,d:Int) = {
      a+b+c+d
    }
    println(fun7(1,2)(3,4))
// val res = fun7(1,2)_
// val res1 = res(3,4)

接下来,使用 klh(3)(_)(4) 的方式调用柯里化函数。其中,第一个参数列表 klh(3) 接受参数 3,并返回一个函数,这个函数需要一个参数 y。而 _ 表示占位符,代表后续需要传入的参数。

然后,将返回的函数赋值给变量 res。最后,通过 res(4) 调用这个函数,将参数 4 传递给 res,并输出结果。

因此,程序的输出结果为 12,即 3 * 4

面向对象

scala和java在继承方面的区别:

1.java只能单继承,scala通过trait实现多重继承(一个类可以继承多个父类),即混入mixin。

2.trait,类似于java中的接口,比接口强大。定义一组方法的规范,但java在接口里不会写实现,而scala包含具体的实现。

3.构造方法,java中的是与类同名的特殊方法,scala中是对象中一个代码块。

4.方法重写:java使用@override注解,scala直接使用override关键字

单例对象

某个对象的实例在整个应用程序的生命周期内是唯一的,类似于java单例模式。使用Singleton Object创建只有一个实例的类。

object SingletonExample {
  def sayHello(): Unit = {
    println("Hello, I am a singleton object!")
  }
}

SingletonExample.sayHello()

伴生对象

scala允许定义和class结构同名的object结构object称之为伴生对象class称之为伴生类当只有object对象时,我们也称这个对象为单例对象、孤立对象

集合

数组 使用 Array 类型表示,可变长度的有序集合,使用索引访问元素  val array = Array(1, 2, 3, 4)

list列表 使用 List 类型表示,不可变的有序集合,可以通过 :: 运算符构建新的列表。 val list = List(1, 2, 3, 4)

set 去重,不可变

/**
  * 可变长Set
  */
import scala.collection.mutable.Set
val set = Set[Int](1,2,3,4,5)
set.add(100)
set.+=(200)
set.+=(1,210,300)
set.foreach(println)

map k-v类型的集合 val map = Map("a" -> 1, "b" -> 2, "c" -> 3)

元组 可以包含不同类型的元素,用()包裹

队列(Queue):使用 Queue 类型表示,可变的先进先出集合。


import scala.collection.mutable.Queue

val queue = Queue(1, 2, 3, 4)

可变集合在操作时可以修改其内容,而不可变集合的操作会返回一个新的集合,原集合保持不变。

scala集合常用的计算函数

  1. sum:计算集合中元素的总和。

val numbers = List(1, 2, 3, 4, 5) val total = numbers.sum println(total) // 输出:15

  1. max:返回集合中的最大值。

val numbers = List(1, 2, 3, 4, 5) val maxNum = numbers.max println(maxNum) // 输出:5

  1. min:返回集合中的最小值。

val numbers = List(1, 2, 3, 4, 5) val minNum = numbers.min println(minNum) // 输出:1

  1. average:计算集合中元素的平均值。

val numbers = List(1, 2, 3, 4, 5) val avg = numbers.sum.toDouble / numbers.length println(avg) // 输出:3.0

  1. count:统计满足特定条件的元素个数。

val numbers = List(1, 2, 3, 4, 5) val evenCount = numbers.count(_ % 2 == 0) println(evenCount) // 输出:2

  1. filter:过滤集合中满足特定条件的元素。

val numbers = List(1, 2, 3, 4, 5) val evenNumbers = numbers.filter(_ % 2 == 0) println(evenNumbers) // 输出:List(2, 4)

  1. map:对集合中的每个元素应用某个函数并返回新的集合。

val numbers = List(1, 2, 3, 4, 5) val doubledNumbers = numbers.map(_ * 2) println(doubledNumbers) // 输出:List(2, 4, 6, 8, 10)

模式匹配

case关键字,每个备选项都包含一个模式和一个或多个表达式  => 隔开了模式和表达式

1.可以匹配值和类型

2.从上到下匹配,匹配上了就不会再往下匹配

3. 都匹配不上时,会匹配case _ 相当于匹配默认值

4.match 的{}  可以去掉

 def matchTest(x:Any) ={
    x match {
      case x:Int=> println("type is Int")
      case 1 => println("result is 1")
      case 2 => println("result is 2")
      case 3=> println("result is 3")
      case 4 => println("result is 4")
      case x:String => println("type is String")
//      case x :Double => println("type is Double")
      case _ => println("no match")
    }
  }
  
}


match {
  case a =>
  case b => 
  case c =>

}

偏函数:方法中没有match只有case

异常

  • 将会发生异常的代码封装在 try 块中。在 try 块之后使用了一个 catch 处理程序来捕获异常。如果发生任何异常,catch处理程序将处理它,程序将不会异常终止。
  • Scala 的异常的工作机制和 Java 一样,但是 Scala 没有“checked(编译期)”异常,即 Scala没有编译异常这个概念,异常都是在运行的时候捕获处理。
  • 异常捕捉的机制与其他语言中一样,如果有异常发生,catch 子句是按次序捕捉的。因此,在 catch 子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,把具体的异常写在后,在 Scala 中也不会报错,但这样是非常不好的编程风格。
  • object Test_Exception {
      def main(args: Array[String]): Unit = {
          try{
            val n =10 /0;
          }catch {
            case e: ArithmeticException =>{
              println("发生算数异常")
            }
            case e: Exception => {
              println("发生一般异常")
            }
          }finally {
            println("处理结束")
          }
      }
    }
    
    
    
    object a {
      def main():Unit = {
        try{
        }
        catch{
            //模式匹配
           case e : 异常处理类  => {}
        }
        finally{
        }
    
      }
    }

隐式转换

类型匹配时,如果找不到合适的类型,会让编译器在作用范围内自动推断类型

隐式方法,即隐式转换函数

使用关键字 implicit修饰,当A对象调用一个方法时,发现A类里面没有这样的方法,但B类中存在这个方法,那么编译器就会在作用域里面找有没有可以将A类对象转换B类对象的隐式转换函数,如果有,就使用,A类就可以调用该方法。

class Animal(name:String){
  def canFly(): Unit ={
    println(s"$name can fly...")
  }
}
class Rabbit(xname:String){
    val name = xname
}
object Lesson_ImplicitFunction {

  implicit def rabbitToAnimal(rabbit:Rabbit):Animal = {
      new Animal(rabbit.name)
  }

  def main(args: Array[String]): Unit = {
    val rabbit = new Rabbit("RABBIT")
    rabbit.canFly()

隐式参数:方法的参数用implicit关键字修饰,必须用KLH的方式书写,写在后面的()中

隐式转换的作用是当调用方法时,不用传参,编译器会自动在作用域范围内搜寻并将隐式参数传入

隐式类:用implicit修饰的类。当A变量没有某个方法,可以在定义一个隐式类,在里面定义方法,然后隐式类的方法传给A去实现

隐式类注意:

1).隐式类必须定义在类,包对象,伴生对象中。

2).隐式类的构造必须只有一个参数,同一个类,包对象,伴生对象中不能出现同类型构造的隐式类。

隐式转换机制即隐式转换函数或类和隐式参数

泛型:通用的类、方法、函数,可以接受各种类型的参数,提高代码灵活性。

class Box[T](value: T) {
  def getValue: T = value
}

val boxInt = new Box[Int](42)
val boxString = new Box[String]("Hello")

val intValue: Int = boxInt.getValue
val stringValue: String = boxString.getValue

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>