scala基础语法

scala基础语法,第1张

参考

  • 《scala编程指南》scala作者参与编写
  • 尚硅谷scala教程

之前接触过python的函数编程,学起来可能更好理解

主要是对照java和python学习,基础语法基本相同,类型后置,自动推断,类型标注有点像python

环境搭建比较简单,scala版本是2.13,直接在idea安装scala插件创建scala项目即可

scala可以两种风格运行,命令式和面向对象

scala提供了repl环境,可以交互式测试

基础语法

val和var的区别,可以理解为final和var的区别

scala对照java容易混淆的语法

var array = new Array[String](3) //定长数组
array(0) = "hello" //实际是调用array.apply(0)函数
array(1) = "hello"
array(2) = "hello"
array(1) = "world"
array.foreach(arg => println(arg + '\n'))

Array<String> array = new Array<String>()
array.add("hello")
array.add("hello")
array.add("hello")
array.set(1,"world")
for(String s : array){
	System.out.println(s)
}
数据类型
  • Scala中一切数据都是对象, 都是Any的子类。
  • Scala中数据类型分为两大类:数值类型(AnyVal) 、引用类型(AnyRef) , 不管是值类型还是引用类型都是对象。
  • Scala数据类型仍然遵守, 低精度的值类型向高精度值类型, 自动转换(隐式转换)
  • Scala中的StringOps是对Java中的String增强
  • Unit:对应Java中的void, 用于方法返回值的位置, 表示方法没有返回值。 Unit是一个数据类型, 只有一个对象就是()。 Void不是数据类型,只是一个关键字
  • Null是一个类型, 只有一个对象就是null。 它是所有引用类型(AnyRef) 的子类。Null 可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
  • Nothing, 是所有数据类型的子类, 主要用在一个函数没有明确返回值时使用, 因为这样我们可以把抛出的返回值, 返回给任何的变量或者函数
  • scala的强制类型转换语法num.toInt,还提供了string转换s1.toInt
  • Scala 中没有运算符,所有运算符都是方法

主要还是学习函数式编程的思想

函数式编程
  • 方法是类中的函数
  • 函数没有重载和重写的概念;方法可以进行重载和重写
  • Scala 语言可以在任何的语法结构中声明任何的语法 ,函数可以嵌套
  • 函数讲究大道至简,能省则省

至简原则细节

  • return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
  • 如果函数体只有一行代码,可以省略花括号
  • 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
  • 如果有 return,则不能省略返回值类型,必须指定
  • 如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
  • Scala 如果期望是无返回值类型,可以省略等号
  • 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
  • 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
  • 如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略

个人觉得就是用数学公式的思路写代码(只不过数学公式没有类型),不写return,没有参数就是单纯的计算过程,举个例子对照一下

scala函数数学函数
//return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
//如果函数体只有一行代码,可以省略花括号
//返回值类型如果能够推断出来,那么可以省略
def f2(s: String): String = s + " jinlian"
def f3(s: String) = s + " jinlian"
如果有 return,则不能省略返回值类型,必须指定
如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
f(x) = 1 + x
Scala 如果期望是无返回值类型,可以省略等号
def f6() { “dalang6” } //注意“dalang6”不是返回值
( 1 + 2 + sin(π) )
//如果函数无参,但是声明了参数列表,那么调用时,小括号可加可不加
def f7() = “dalang7”
println(f7)
//如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
def f8 = “dalang”
println(f8)
x = 1
//如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
def f10(f: String => Unit) = {
f(“”)
}
println(f10((x: String) => {
println(“wusong”)
}))
f(x) = 1 + x
g(x) = f(x) + x
高阶函数

在 Scala 中,函数是一等公民

  • 函数可以作为值进行传递

  • 在被调用函数 foo 后面加上 _,相当于把函数 foo 当成一个整体,传递给变量 val f1 = foo _

  • 如果明确变量类型,那么不使用下划线也可以将函数作为整体传递给变量 var f2:()=>Int = foo

  • 函数可以作为参数进行传递 def f1(f: (Int, Int) => Int): Int

  • 函数可以作为函数返回值返回

匿名函数

定义函数

def operation(arr: Array[Int], op: Int => Int) = {
	for (elem <- arr) yield op(elem)
}
def op(ele: Int): Int = {
	ele + 1
}
val arr = operation(Array(1, 2, 3, 4), op)
println(arr.mkString(","))

def calculator(a: Int, b: Int, op: (Int, Int) => Int): Int = {
	op(a, b)
}

//匿名函数写法
val arr2 = operation(Array(1, 2, 3, 4), (ele) => { ele + 1 })
  • 参数的类型可以省略,会根据形参进行自动的推导

    val arr2 = operation(Array(1, 2, 3, 4), (ele) => { ele + 1 })
    calculator(2, 3, (x , y) => x + y)
    
  • 类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。

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

    val arr3 = operation(Array(1, 2, 3, 4), ele => ele + 1 )
    
  • 如果参数只出现一次,则参数省略且后面参数可以用_代替

    val arr5 = operation(Array(1, 2, 3, 4), _ + 1)
    calculator(2, 3, _ + _)
    

模拟 Map 映射、 Filter 过滤、 Reduce 聚合Demo

object hello {

  def main(args: Array[String]): Unit = {
    var a = Array(1, 2, 3, 4, 5, 6)
    var arr1 = map(a, ele => ele + 1)
    println(arr1.mkString(","))
    var arr2 = filter(a,ele => ele % 2 == 1)
    println(arr2.mkString(","))
    var arr3 = reduce(a, _ * _)
    //var arr3 = reduce(a, (x, y) => x * y)
    println(arr3)
  }

  def map(arr: Array[Int], op: Int => Int) = {
    for (ele <- arr) yield op(ele)
  }

  def filter(arr: Array[Int], op: Int => Boolean) = {
    var arrtmp = ArrayBuffer[Int]();
    for (ele <- arr if op(ele)) {
      arrtmp.append(ele)
    }
    arrtmp.toArray
  }

  def reduce(arr:Array[Int],op:(Int,Int) => Int)= {
    var init :Int = arr(0)
    for(index <- 1 until arr.length){
      init = op(init,arr(index))
    }
    init
  }
}
柯里化&闭包

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

def main(args: Array[String]): Unit = {
	def f1()={
		var a:Int = 10
		def f2(b:Int)={
			a + b
		}
		f2 _
	}

val f = f1()
println(f(3))
println(f1()(3))

函数柯里化:把一个参数列表的多个参数,变成多个参数列表 (柯里化必然闭包)

def f3(a:Int)(b:Int)={
	a + b
}
println(f3()(3))
值调用和名调用
object TestFunction {
	def main(args: Array[String]): Unit = {
		var i:Int = 1
		myWhile(i <= 10){
			println(i)
			i +=1
		}
	}
    //柯里化,大括号里的内容作为第二个参数
	def myWhile(condition: =>Boolean)(op: =>Unit):Unit={
		if (condition){
			op
			myWhile(condition)(op)
		}
	}
}
惰性加载

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

注意:lazy 不能修饰 var 类型的变量

def main(args: Array[String]): Unit = {
	lazy val res = sum(10, 30)
	println("----------------")
	println("res=" + res)
}
def sum(n1: Int, n2: Int): Int = {
	println("sum is executed")
	return n1 + n2
}  

output:
----------------
sum is executed
res=40
面向对象

语法上和java类似,细节上和C++类似

包管理

Scala 有两种包的管理风格 ,一种类似java方式,一种类似C#方式

类似C#方式,子包中的类可以直接访问父包中的内容,而无需导包,但父包访问子包需要导包

//类似C#,子包中的类可以直接访问父包中的内容,而无需导包,但父包访问子包需要导包
package com{
	package zhaojie{
		package scala{
		}
	}
}

scala程序运行必须提供独立对象并包括main方法作为入口函数,但是scala同样提供了App特质简化这一 *** 作,直接在{}中写main函数的逻辑

object Demo extends App {
  var argsc = new Array[String](3)
  argsc(0) = "hello"
  argsc(1) = "world"
  argsc(2) = "ohhhh"
  argsc.foreach(arg => println(arg + ":" + ChecksumAccumulator.calculate(arg)))
}

导包

包对象

类文件分成两部分class和object

因为scala的类中不允许有静态成员,因此出现了单例对象object,单例对象和类共用一个名字时,object是class的伴生对象,class是object的伴生类(似乎是将类的静态部分单独提取到object中)

object和class可以相互访问各自的私有成员,必须在一个文件中定义class和object

没有伴生类的object是孤立对象,常用作工具类和应用程序入口(main方法)

举例,ChecksumAccumulator例程是通过object的calculate计算校验和

package com.zhaojie.demo

import scala.collection.mutable

class ChecksumAccumulator {
  private var sum = 0
  def add(b: Byte) = sum += b
  def checksum() = ~(sum & 0xFF) + 1
}

object ChecksumAccumulator {
  private val cache = mutable.Map.empty[String, Int]
  //函数返回值简写
  def calculate(s: String) =
    if (cache.contains(s)) {
      cache(s)
    } else {
      val acc = new ChecksumAccumulator
      for (c <- s) {
        acc.add(c.toByte)
      }
      val cs = acc.checksum()
      //不能写成cache(s) += cs 会报空指针
      cache += (s -> cs)
      cs
    }
}
访问权限
  • Scala 中属性和方法的默认访问权限为 public,但 Scala 中无 public 关键字。
  • private 为私有权限,只在类的内部和伴生对象中可用。
  • protected 为受保护权限,Scala 中受保护权限比 Java 中更严格,同类、子类可以访问,同包无法访问。
  • private[包名]增加包访问权限,包名下的其他类也可以使用
多态

Scala 中属性和方法都是动态绑定,而 Java 中只有方法为动态绑定

构造器

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

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

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

class 类名(形参列表) { // 主构造器
	// 类体
	def this(形参列表) { // 辅助构造器
	}
	def this(形参列表) { //辅助构造器可以有多个...
	}
}

写一个单例模式

注意下划线 _ 的特殊用法,Scala中必须要显式指定类成员的初值,或者使用下划线让编译器自动设置初始值

class Person private(val name: String, val age: Int) {
}

object Person{
  def main(args: Array[String]): Unit = {
    var p = Person.getInstance()
  }
  private val person = new Person("zhangsan",19)
  def getInstance() : Person = person
}

object Person {
  def main(args: Array[String]): Unit = {
    var p = Person.getInstance()
  }
  //自动设置初始值
  private var person: Person = _
  def getInstance(): Person = {
    if (person == null) {
      person = new Person("zhangsan", 10)
    }
    person
  }
}
继承和重写

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

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

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

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

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

特质

特质=抽象类+接口

Scala 语言中,采用特质 trait(特征)来代替接口的概念

Scala 中的 trait 中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质

基本语法
没有父类:class 类名 extends 特质 1 with 特质 2 with 特质 3 …
有父类:class 类名 extends 父类 with 特质 1 with 特质 2 with 特质 3…

所有的 Java 接口都可以当做 Scala 特质使用

动态混入:可灵活的扩展类的功能

  • 动态混入:创建对象时混入 trait,而无需使类混入该 trait
  • 如果混入的 trait 中有未实现的方法,则需要实现
trait SexTrait {
	var sex: String
}
val t2 = new Teacher with SexTrait {
	override var sex: String = "male"
}

由于一个类可以混入(mixin)多个 trait,且 trait 中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题

  • 一个类(Sub)混入的两个 trait(TraitA,TraitB)中具有相同的具体方法,且两个 trait 之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法
  • 一个类(Sub)混入的两个 trait(TraitA,TraitB)中具有相同的具体方法,且两个 trait 继承自相同的 trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略,就是将混入的多个 trait 中的冲突方法叠加起来

注意:super,不是表示其父特质对象,MyClass 中的 super 指代 Color,Color 中的 super 指代 Category,Category 中的 super指代 Ball

  • 调用某个指定的混入特质中的方法,可以增加约束:super[] super[Category].describe()
集合

这篇blog总结的很全面,Scala 可变集合体系、不可变集合体系 详解

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

不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
集合高级函数

reduceright和foldright比较特殊

object test {
    def main(args: Array[String]): Unit = {
      val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
      val nestedList: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
      val wordList: List[String] = List("hello world", "hello atguigu", "hello scala")
      //(1)过滤
      println(list.filter(x => x % 2 == 0))
      //(2)转化/映射
      println(list.map(x => x + 1))
      //(3)扁平化
      println(nestedList.flatten)
      //(4)扁平化+映射 注:flatMap 相当于先进行 map  *** 作,在进行 flatten *** 作
      println(wordList.flatMap(x => x.split(" ")))
      //(5)分组
      println(list.groupBy(x => x % 2))
        
      val list = List(1,2,3,4)
      val i0 = list.reduceRight((x,y) => x-y) // 1-2-3-4
      val i1 = list.reduceRight((x,y) => x-y) // 1-(2-(3-4))
      //fold和reduce差不多只是多了初值
      val i2 = list.foldLeft(1)((x,y)=>x-y) //1-1-2-3-4
      val i3 = list.foldRight(10)((x,y)=>x-y) // 1-(2-(3-(4-10)))
    }
}

output:
List(2, 4, 6, 8)
List(2, 3, 4, 5, 6, 7, 8, 9, 10)
List(1, 2, 3, 4, 5, 6, 7, 8, 9)
List(hello, world, hello, atguigu, hello, scala)
Map(1 -> List(1, 3, 5, 7, 9), 0 -> List(2, 4, 6, 8))

wordcount练习
object TestWordCount {
  def main(args: Array[String]): Unit = { // 单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果
    val stringList = List("Hello Scala Hbase kafka", "Hell Scala Hbase", " Hello Scala", "Hello")
    // 1) 将每一个字符串转换成一个一个单词
    val wordList: List[String] = stringList.flatMap(str => str.split(" "))
    //println(wordList)
    // 2) 将相同的单词放置在一起
    val wordToWordsMap: Map[String, List[String]] = wordList.groupBy(word => word)
    //println(wordToWordsMap)
    // 3) 对相同的单词进行计数
    // (word, list) => (word, count)
    val wordToCountMap: Map[String, Int] = wordToWordsMap.map(tuple => (tuple._1, tuple._2.size))
    // 4) 对计数完成后的结果进行排序(降序)
    val sortList: List[(String, Int)] = wordToCountMap.toList.sortWith {
      (left, right) => {
        left._2 > right._2
      }
    }
    // 5) 对排序后的结果取前 3 名
    val resultList: List[(String, Int)] = sortList.take(3)
    println(resultList)
  }
}
模式匹配 样例类

匹配类时

case class Person (name: String, age: Int)
  • 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如 apply、 unapply、 toString、 equals、 hashCode 和 copy。
  • 样例类是为模式匹配而优化的类,因为其默认提供了 unapply 方法,因此,样例类可以直接使用模式匹配,而无需自己实现 unapply 方法。
  • 构造器中的每一个参数都成为 val,除非它被显式地声明为 var

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/738367.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-28
下一篇 2022-04-28

发表评论

登录后才能评论

评论列表(0条)

保存