scala

Scala

默认不需要语句终结符”;“,它将每一行作为一个语句。如果一行放多条语句,则要使用语句终结符。

变量

可变变量,使用 var 定义;不可变变量,使用 val 定义。可以手动指定,不指定时自动推导。

var a = 2;
val b = 4;
var c: Int = 1;

数据类型

基本数据类型: Byte、 Char、 Short、 I nt、 Long、 Float、 Double、 Boolean

增强版数据类型: StringOps、 RichInt、 RichDouble、 RichChar 等...

操作符与java类似,但没有 ++ --

选择

Scala 中 if 表达式有返回值,就是if或else中最后一行返回的值。所以if表达式可以赋值给一个变量。

var res = if (true) 1 else 0;

// 当if与else返回不同类型数据时,变量自动推到为二者的公共父类。这里res为Any类型
var res = if (age > 18) "old" else 0;

// 选择语句可能返回空,此处res类型也为Any
var res = if (age > 18) "old"

循环

for while

val n = 10
for(i <- 1 to n) {  // <- 表示迭代右侧列表[1,10]
  print(i)          // 打印不输出换行符
  println(i)        // 打印并输出换行符
}

for (i <- 1 until n) print(i) // [1, 9]
for (i <- "hello scala") print(i) // 迭代字符
var n = 10
while (n > 10) {
  println(n)
  n -= 1
}

高级for循环

if 守卫模式

for (i <- 1 to 10 if i % 2 == 0) // 只输出偶数
  println(i)

for 推导式

// 返回一个Vector(2,4,6,8,10,12,14,16,18,20)
var res = for (i <- 1 to 10) yield i * 2

 集合

分可变集合(scala.collection.mutable)、不可变集合(scala.collection.immutable)(默认使用不可变的,默认导入不可变类型集合的包)

HashSet、SortedSet 都存在可变和不可变类型,LinkedHashSet 只有可变的

val s = Set(1,2,3)
// s: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
// s += 4  // error

val s = scala.collection.mutable.Set(1,2,3)  // 可变 Set
s += 4 // 正常

val s = scala.collection.mutable.HashSet[Int]()
s += 1  // Set(1)
s += 2  // Set(1,2)
s += (3)  // Set(2,1,3)


for (i <- s) println(i)

List 只有不可变,有 head tail :: 三个特殊操作

val l = List(1,2,3,4)
l.head  // 1
l.tail  // List(2,3,4)
l.head :: l.tail // List(1,2,3,4)

ListBuffer 可以支持动态增加或移除元素

val lb = scala.collection.mutable.ListBuffer[Int]()
lb += 1
lb += 2 // ListBuffer(1,2)
ln -= 2 // ListBuffer(1)

 Map (SortedMap只有不可变类型)

val ages = Map("jack" -> 20, "tom"->21)  // 默认不可变类型
ages("jack")                             // 20

val ages = scala.collection.mutable.Map(("jack", 20), ("tom", 21)) // 另一种初始化方式
ages("jack") = 21                        // 更新值
ages += ("hehe"->12, "haha"->21)         // 添加新元素
ages -= "hehe"

ages("xxx")                              // 查找不存在key时抛出异常

val age = ages.getOrElse("jack", 0)
// 等价于下式
val age = if(ages.contains("jack")) ages("jack") else 0

for ((key, value) <- ages)               // 存在 ages.keySet ages.values
  println(key + "" + value)

 Array 长度不可变,底层就是java的数组,初始化后长度就固定下来了, 元素全部根据其类型初 始化, 也可以直接使用Array()创建数组, 元素类型自动推断

val a = new Array[Int](5)
// Array(0,0,0,0,0)
a(0) = 1

val a = Array("Hello", "scala")

a.sum
a.max
a.min
scala.util.Sorting.quickSort(a) // a被快速排序

ArrayBuffer 长度可变

val b = ArrayBuffer[Int]()
b += 1
b += (2,3,4,5)
b.insert(3,30) // ArrayBuffer(1,2,3,30,4,5)
b.remove(1)    // ArrayBuffer(1,3,30,4,5)
b.toArray()    // 转为 Array
a.toBuffer()   // Array 转 ArrayBuffer

Tuple 元组,与Array类似,都是不可变。但是它可以包含不同类型的元素,Array不行;Array可更新内部值,Tuple不可以更新。元组脚标从1开始

val tup = Tuple3(1, 2.2, "str") // Tuple后的数字必须和元素长度一致
val t = (1, 3.14, "string")
t._1 // 1
t._3 // string

函数

要求必须给出所有参数类型,但返回值类型不是必须的,因为可以自动推断。不需要return,最后一行语句就是返回值。

定义:

def sayHello(name: String) = print("hello" + name)

def sayHello(name: String, age: Int) = {
  println("name:" + name + "age:" + age)
  age   // 即为返回值
}

参数:

  1. 默认参数: 有时我们调用某些函数时, 不希望给出参数的具体 值, 而希望使用参数自身默认的值
  2. 带名参数: 在调用函数时, 也可以不按照函数定义的参数顺序 来传递参数, 而是使用带名参数的方式来传递
  3. 可变参数: 有时我们需要将函数定义为参数个数可变的形式, 则此时可以使用可变参数定义函数
def sayHello(fName: String, mName: String = "mid", lName: String = "last") = {}

sayHello("fName") // 只需传入一个参数就可以运行
sayHello(fName="jack", lName="Tom", mName="Mick")  // 使用参数的名字指定参数

 

def sum(nums: Int*) = {
  var res = 0
  for (num <- nums)
    res += num
  res
}

 过程

特殊的函数,定义函数时, 如果函数体直接在花括号里面而没有使用=连接, 则函数的返回值类型就是Unit, 这样的函数称之为过程

过程通常用于不需要返回值的函数

过程还有一种写法, 就是将函数的返回值类型显式定义为Unit

// 函数,返回值为 "hello " + name
def sayHello(name: String) = "hello " + name
def sayHello(name: String): String = "hello " + name

// 过程,没有返回值
def sayHello(name: String) { "hello " + name }
def sayHello(name: String): Unit = "hello " + name

lazy

Scala提供了lazy特性, 如果将一个变量声明为lazy, 则只有 在第一次使用该变量时, 变量对应的表达式才会发生计算

适用于打开文件、获取链接等耗时操作。

lazy val lines = fromFile("D://test.txt").mkString // 这条命令用于打开文件,只有使用lines变量时才会真正打开文件

面向对象

类 class

class Person {
	var name = "scala"

    // 定义时有括号,调用时必须有括号(不同版本可能不同)
    def sayHello() = {
        println("hello "+ name)
    }

    // 定义时没括号,调用时也没括号
    def getName = name
}

val p = new Person()
p.sayHello()
p.getName

构造函数

  1. 主constructor: 类似Java的默认构造函数 this()
    class Student(val name: String = "none", val age: Int = 0) { // 这个大括号内就是主构造函数
      println("This is main constructor")
    }

    1. Scala的主constructor是与类名放在一起的, 与Java不同
    2. 注意: 在类中, 没有定义在任何方法或者是代码块之中的代码 就是主constructor的代码
    3. 主constructor中可以通过默认参数, 来给参数设置默认值
  2. 辅助constructor: 类似Java的重载构造函数 this(name,age)
    class Student{
      var name = "jack" // 此处即为主构造函数
      var age = 10
    
      def this(name: String) = { // 辅助构造函数,必须调用主构造函数
        this()
        this.name = name
      }
    
      def this(name: String, age: Int) = { // 辅助构造函数,调用其他辅助构造函数,间接调用主构造函数
        this(name: String)
        this.age = age
      }
    }
    1. Scala可以给类定义多个辅助constructor, 类似于Java中的 构造函数重载
    2. 辅助constructor可以互相调用, 第一行必须调用主constructor

 

对象 object

  • java中object是new出来的,scala中可以new也可以自己定义。
  • 相当于class的单个实例, 通常在里面放一些静态的 field或者method
  • object不能定义带参数constructor, 只有空参constructor
  • 第一次调用object的方法时, 会执行object的constructor 仅执行一次
  • object通常作为单例模式的实现, 或者放class的静态成员
  • object不能被new, 可以直接使用
// 定义 object
object person {
  var age = 1
  println("main constructor")

  def getAge = age
}

// 使用
person.age    // 第一次调用会调用主构造方法
person.getAge

伴生对象

  • 如果有一个class, 还有一个与class同名的object, 那么就称 这个object是class的 伴生对象 , class是object的 伴生类
  • 伴生类和伴生对象必须存放在一个.scala文件之中
  • 伴生类和伴生对象最大特点在于可以互相访问private field
object Person {
  var age = 1
  private val money = 1  // 私有变量
  println("main constructor")

  def getAge = age
}
class Person(name: String = "", age: Int = 0) {
  def sayHello = {
    println("name: " + name + " age: " + age + " money: " + Person.money) // 此处调用了伴生对象的私有变量
  }
}

apply

  • apply是object中非常重要的一个特殊方法, 通常在伴生对象 中实现apply方法, 并在其中实现构造伴生类对象的功能
  • 在创建对象时, 就不需要使用new Class的方式, 而是使用 Class()的方式, 隐式调用伴生对象的apply方法, 这样会让 对象创建更加简洁
class Person(val name: String) {
  println("name is : " + name)
}
object Person{
  def apply(name: String) = {
    println("apply exec...")
    new Person(name)
  }
}

Person("Tom") // 此处调用 object Person 中的apply方法,返回一个 new Person(name)

main

  • 和Java一样, 在Scala中如果要运行一个应用程序, 必须 有一个main方法,作为入口
  • Scala中的main方法必须定义在object中, 格式为:def main(args: Array[String])
package org.example

object mainDemo {
  /**
   * main 方法只能定义再object中,不能定义在class中
   */
  def main(args: Array[String]):Unit = {
    println("hello scala")
  }
}

接口 trait

  • Scala中的trait类似于Java中的interface
  • 在triat中可以定义抽象方法
  • 类可以使用 extends 关键字继承trait, 无论继承类还是trait 统一都是extends
  • 类继承trait后, 必须实现trait中的抽象方法, 实现时不需要使 用override关键字
  • scala不支持对类进行多继承, 但是支持对trait进行多重继承, 使用with关键字即可

 

package org.example

object PersonDemo {
  def main(args: Array[String]): Unit = {
    val p1 = new Person("tom")
    val p2 = new Person("jack")
    p1.sayHello(p2.name)
    p1.makeFriend(p2)
  }
}

// 定义接口
trait HelloTrait {
  def sayHello(name: String):Unit
}

trait FriendTrait {
  def makeFriend(p: Person): Unit
}

// 继承多个接口
class Person(val name: String) extends HelloTrait with FriendTrait {
  override def sayHello(name: String): Unit = {  // 重写函数
    println("hello " + name)
  }

  override def makeFriend(p: Person): Unit = {
    println("make friend with " + p)
  }
}

 

case class 样例类

  • 称为样例类, 类似于Java中的JavaBean, 只定 义field, Scala自动提供getter和setter方法, 没有method
  • case class的主构造函数接收的参数通常不需要使用var或val 修饰Scala会自动使用val修饰
  • Scala 自动为 case class定义了伴生对象, 也就是object, 并 且定义了apply()方法, 该方法接收主构造函数中相同的参数 并返回case class对象

 

函数式编程

 Scala是一门既面向对象, 又面向过程的语言

 在Scala中, 函数与类、 对象一样, 都是一等公民

 函数赋值给变量

  •  Scala中的函数是一等公民, 可以独立定义, 独立存在, 而且 可以直接将函数作为值赋值给变量
  •  Scala的语法规定: 将函数赋值给变量时, 必须在函数后面加 上空格和下划线 (在scala3中不是必须)
def say(name: String) = { println("hello" + name) }
val sayFunc = say
val sayFunc2 = say _
// 以下三者效果相同,都能得到 helloxxx
say("xxx")
sayFunc("xxx")
sayFunc2("xxx")

 匿名函数

  •  Scala中的函数也可以不需要命名, 这种函数称为匿名函数
  •  匿名函数的语法格式: (参数名: 参数类型) => 函数体
  •  可以将匿名函数直接赋值给某个变量
val sayFunc = (name: String) => println("hello " + name)

val sayFunc = (name: String) => {
  println("hello " + name)
}

高阶函数

  • 由于函数是一等公民, 所以我们可以直接将某个函数作为参 数传入其它函数
  • 接收其它函数作为当前函数的参数, 当前函数也被称作高阶 函数 (higher-order function)
  • 高阶函数可以自动推断出它里面函数的参数类型,对于只有 一个参数的函数, 还可以省去小括号
val sayFunc = (name: String) => println("hello " + name)
    
def exec(func: (String)=>Unit, name: String) = {
  func(name)
}

exec((name: String) => println("hello " + name), "xxx") // 直接将匿名函数当作参数
exec(name => println("hello " + name), "xxx")           // 匿名函数的参数类型可以自动推导,只有一个参数时可以省略小括号
exec(sayFunc, "xxx")
// hello xxx

常用高阶函数

  1.  map       一进一出
    Array(1,2,3,4,5).map(num => num*2)
    Array(1,2,3,4,5).map(_ * 2) // 简写形式
    // Array[Int] = Array(2, 4, 6, 8, 10)
  2. flatMap  一进多出
    Array("hello world", "hello scala").flatMap(line => line.split(" "))
    // Array(hello, world, hello, scala)
  3. foreach  迭代
    Array(1,2,3,4,5).foreach(println(_)) // 简写形式
  4. filter       过滤
    Array(1,2,3,4,5).filter(num => num % 2 == 0)
    Array(1,2,3,4,5).filter(_ % 2 == 0)
    // Array(2,4)
  5. reduceLeft     聚合操作
    Array(1,2,3,4,5).reductLeft((t1, t2) => t1 + t2)
    Array(1,2,3,4,5).reductLeft(_ + _)  // 简写,只有逻辑特别简单时才能使用
    // 15

高级特性

模式匹配

  • Scala没有Java中的switch case语法, 但是, Scala提供了更 加强大的match case语法, 即模式匹配
  • Java的switch case仅能匹配变量的值, Scala的match case可 以匹配各种情况, 比如: 变量的类型、 集合的元素、 有值没值
  • 格式:变量 match { case 值 => 代码 }

变量值匹配

def func(day: Int) = {
  day match {
    case 1 => println("Monday")
    case 2 => println("Tuesday")
    case 3 => println("Wednesday")
    case _ => println("other")
  }
}

变量类型匹配

def func(e: Exception) = {
  e match{
      case e1: FileNotFoundException => println("FileNotFoundException")
      case e2: IOException => println("IOException")
      case _: Exception => println("Exception")
  }
}

try{
  val lines = scala.io.Source.fromFile("D://testxxx.txt").mkString
} catch {
  case ex: FileNotFoundException => println("no file")
  case ex: IOException => println("io exception")
  case ex: Exception => println("exception")
}

case class 与模式匹配

class Person
case class Teacher(name: String, sub: String) extends Person
case class Student(name: String, cla: String) extends Person

def check(p: Person) = {
  p match {
    case Teacher(name, sub) => println("teacher: name is " + name + " sub is " + sub)
    case Student(name, cla) => println("student: name is " + name + " cla is " + cla)
    case _ => println("other")
  }
}

Option与模式匹配

  • Option有两种值, Some->表示有值, None->表示没有值
  • Option通常会用于模式匹配中, 用于判断某个变量是有值还 是没有值, 这比null更加简洁明了
    val m = Map(("a",1))  // Map[String, Int] = Map(a -> 1)
    m.get("a")            // Option[Int] = Some(1)
    m.get("b")            // Option[Int] = None
    // 使用map的get方法得到的就是Option对象,通过判断是Some还是None得知是否有值
val m = Map(("a",1))
    
def getNum(str: String) = {
  val num = m.get(str)
  num match {
    case Some(num) => println("get num: " + num)
    case None => println("None")
  }
}

 

隐式转换

  • Scala的隐式转换, 允许手动指定将某种类型的对象转换成其 它类型的对象
  • Scala的隐式转换, 最核心的就是定义隐式转换函数, 即 implicit conversion function
  • 隐式转换函数与普通函数唯一的语法区别是要以implicit开 头而且最好要定义函数返回类型

Scala默认会使用两种隐式转换

  1. 源类型, 或者目标类型的伴生对象里面的隐式转换函数
  2. 当前程序作用域内可以用唯一标识符表示的隐式转换函数

如果隐式转换函数不在上述两种情况下的话, 那么就必须手动 使用import语法引入某个包下的隐式转换函数

建议: 仅仅在需要进行隐式转换的地方

 

class Cat(val name: String) {
  def catchMouse() = {
    println(name + " catch mouse")
  }
}


class Dog(val name: String)


implicit def object2Cat(obj: Object): Cat = {
  if (obj.getClass == classOf[Dog]) {
    val dog = obj.asInstanceOf[Dog]
    new Cat(dog.name)
  }
  else Nil
}

val d = new Dog("狗")
d.catchMouse()    // 隐式转换调用方法
// 狗 catch mouse

 

posted @ 2022-06-10 11:52  某某人8265  阅读(73)  评论(0编辑  收藏  举报