为什么要学习Scala语言
- 最直接的一点就是因为我们后面要学的Spark框架需要用到Scala这门语言,但是Spark其实是同时支持Scala语言和Java语言的,为什么非要学Scala呢,使用java它难道不香吗?
- 这就要说第二点了:scala相比java代码量更少,更适合函数式编程。
什么是Scala
Scala是一门多范式的编程语言,它是一种类似Java的编程语言,它设计的初衷是为了实现可伸缩的语言、并集成面向对象编程和函数式编程的各种特性
Scala基于Java虚拟机,也就是基于JVM的一门编程语言。所有Scala代码,都需要编译为字节码,然后交由Java虚拟机来运行
Scala和Java可以无缝相互操作,Scala可以任意调用Java代码,这个特性是非常好的
Scala环境安装配置
注意:由于Scala是基于Java虚拟机的,所以使用 Scala 之前必须先安装 Java,Java我们已经安装过了。
那在这里我们先到官网下载Scala安装包
下载地址,这里我们使用2.12.11
版本
Scala命令行
scala
Scala命令行也称为Scala解释器(REPL),它会快速编译Scala代码为字节码,然后交给JVM来执行
这里的REPL表示:Read(取值)-> Evaluation(求值)-> Print(打印)-> Loop(循环)
在Scala命令行内,输入Scala代码,解释器会直接返回结果
如果你没有指定变量来存放计算的值,那么值默认的名称会显示为res开头的变量,而且会显示结果的数据类型
如果想在scala REPL中执行多行代码,该如何操作?
使用 :paste
和 ctrl+D
的方式
:paste 表示代码块的开始
ctrl+D 表示代码块的结束
IDEA开发配置
安装Scala插件,添加Scala的SDK
之后就可以插件Scala类了。
Scala的基本使用
scala很多语法和kotlin都很类似,如定义变量,不需要分号,函数等。
变量
Scala中的变量分为两种:可变 var 和 不可变 val
可变var
:可以随时修改var声明的变量的值
不可变val
:val声明的变量,值不能被修改,否则会报错: error: reassignment to val
注意:在实际工作中,针对一些不需要改变值的变量,通常建议使用val,这样可以不用担心值被错误的修改(等于java中的final类型)。这样可以提高系统的稳定性和健壮性!
无论声明val变量,还是声明var变量,都可以手动指定变量的类型,如果不指定,Scala会自动根据值,进行类型推断
val c = 1 等价于 val c: Int = 1
数据类型
Scala中的数据类型可以分为两种,基本数据类型和增强版数据类型
- 基本数据类型有: Byte、Char、Short、Int、Long、Float、Double、Boolean
- 增强版数据类型有: StringOps、RichInt、RichDouble、RichChar 等
scala使用这些增强版数据类给基本数据类型增加了上百种增强的功能
例如:RichInt提供的有一个to函数, 1.to(10) ,此处Int会先隐式转换为RichInt,然后再调用其to函数
val inclusive = 1 to 10
for (i <- inclusive) {
print(i + " ")
}
println()
val inclusive2 = 1.to(10)
for (i <- inclusive2) {
print(i + " ")
}
操作符
Scala的算术操作符与Java的算术操作符没有什么区别
比如 +、-、*、/、% 等,以及 &、|、^、>>、<< 等
注意:Scala中没有提供++、–操作符
我们只能使用+和-,比如count = 1,count++是错误的,必须写做count += 1
if 表达式
在Scala中,if表达式是有返回值的,就是if或者else中最后一行语句返回的值,这一点和java中的if是不一
样的,java中的if表达式是没有返回值的
例如: val age = 20; if (age > 18) 1 else 0
val score = 90
val level = if (score > 90) "A" else "B"
println(level)
由于if表达式是有值的,而if和else子句的值的类型可能还不一样,此时if表达式的值是什么类型呢?
注意:Scala会自动进行推断,取两个类型的公共父类型
如果if后面没有跟else,则默认else的值是Unit,也可以用()表示,类似于java中的void或者null
语句终结符
Scala默认不需要语句终结符,它将每一行作为一个语句
如果一行要放多条语句,则前面的语句必须使用语句终结符,语句终结符和Java中的一样,就是我们平时使用的分号
循环
for (i <- 5 until 10) {
print(i + " ")
}
//迭代字符串
for (i <- "hello") {
print(i + " ")
}
println()
var n = 10
while (n > 0) {
print(n + " ")
n -= 1
}
1 to 10 可以获取1~10之间的所有数字
1 until 10可以获取1~9之间的所有数字
高级for循环
//if守卫
for (i <- 1 to 10 if i % 2 == 0) {
print(i + " ")
}
//for推导式
val ages = for (i <- 1 to 5) yield i * i
println(ages.toList)
Scala的集合体系
Scala中的集合是分成可变和不可变两类集合的
- 其中可变集合就是说,集合的元素可以动态修改,在 scala.collection.mutable 这个包下面
- 而不可变集合就是说,集合的元素在初始化之后,就无法修改了,在 scala.collection.immutable 这个包下面
//不可变集合
val set = Set(1, 1, 2, 3)
println(set)
//可变集合
val set2 = scala.collection.mutable.Set(1, 2, 3)
set2.add(4)
println(set2)
//不可变列表
val list = List(1, 2, 3)
println(list)
//可变列表
val listBuffer = scala.collection.mutable.ListBuffer(1, 2, 3)
listBuffer.append(4)
println(listBuffer.toList)
//不可变Map
val map = Map("name" -> "lisi", "age" -> 20)
println(map)
val map2 = scala.collection.mutable.Map("name" -> "lisi", "age" -> 20)
map2.put("address", "上海")
println(map2)
println(map2.get("name1")) # 如果存在,返回的是Option对象,类似java中的Optional,get()方法获取数据
//println(map2("name1")) 不存在抛异常
println(map2.getOrElse("name1", "lisi2"))
//不可变数组 求和 排序
val array = Array(2, 1, 3)
println(array.sum)
println(array.max)
println(array(0))
Sorting.quickSort(array)
println(array.toList)
//可变数组
val arrayBuffer = scala.collection.mutable.ArrayBuffer(2, 1, 3)
arrayBuffer.append(4)
println(arrayBuffer.toList)
//元组 数据不可变
val tuple = Tuple3(1, 2, "12")
println(tuple)
Scala中函数的使用
在Scala中定义函数需要使用 def 关键字,函数包括函数名、参数、函数体
Scala要求必须给出函数所有参数的类型,但是函数返回值的类型不是必须的,因为Scala可以自己根据函数体中的表达式推断出返回值类型。
函数中最后一行代码的返回值就是整个函数的返回值,不需要使用return,这一点与Java不同,java中函数的返回值是必须要使用return的
//单行函数
def sum(a: Int, b: Int): Int = a + b
//多行函数 包含默认参数
def sum2(a: Int, b: Int = 0): Int = {
//默认最后一行代码的返回值就是函数的返回值
a + b
}
//可变参数
def sum3(nums: Int*): Int = {
var sum = 0
for (num <- nums) {
sum += num
}
sum
}
//特殊的函数-过程 没有使用= 返回值就是Unit
def sum4(a: Int, b: Int) {
println(a + b)
}
//特殊的函数-过程
def sum5(a: Int, b: Int): Unit = {
println(a + b)
}
println(sum(1, 4))
//使用参数名来传参
println(sum2(b = 6, a = 5))
println(sum3(1, 2, 3, 4))
过程通常用于不需要返回值的函数
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 val fileSource = Source.fromFile("C:/D-myfiles/testjar/test.txt")
val lines = fileSource.mkString
fileSource.close()
println(lines)
如果将一个变量声明为lazy,则只有在第一次使用该变量时,变量对应的表达式才会发生计算
即使文件不存在,代码也不会报错,只有变量使用时才会报错,这就是lazy这个特性。
Scala面向对象编程
Scala中类和java中的类基本是类似的
Scala中的对象时需要定义的,而java中的对象是通过class new出来的
Scala中的接口是trait,java中的接口是interface
类-class
object Demo2 {
def main(args: Array[String]): Unit = {
val person = new Person
person.printName()
person.printName
}
class Person {
var name = "lisi"
def printName(): Unit = {
println(name)
}
}
}
如果在定义方法的时候指定了(),那么在调用的时候()可写可不写,如果在定义方法的时候
没指定(),则调用方法时肯定不能带()
Scala类中的构造函数可以分为主构造函数和辅助构造函数
这两种构造函数有什么区别呢?
主constructor:类似Java的默认构造函数 this()
辅助constructor:类似Java的重载构造函数 this(name,age)
object Demo2 {
def main(args: Array[String]): Unit = {
val student = new Student("lisi")
println(student)
val student2 = new Student("lisi", 23)
println(student2)
}
class Student(name: String) {
var age = 0
def this(name: String, age: Int) {
this(name)
this.age = age
}
override def toString: String = {
"%s,%s".format(name, age)
}
}
}
Scala中,可以给类定义多个辅助constructor,类似于java中的构造函数重载
辅助constructor之间可以互相调用,但是第一行必须调用主constructor
对象-object
object:相当于class的单个实例,通常在里面放一些静态的field或者method
object不能定义带参数的constructor,只有空参的constructor
第一次调用object的方法时,会执行object的constructor,也就是执行object内部不在任何方法中的代码,因为它只有空参的构造函数
但是注意,object的constructor的代码只会在他第一次被调用时执行一次,以后再次调用就不会再执行了
object通常用于作为单例模式的实现,或者放class的一些静态成员,比如工具方法
object可以直接使用,不能new
object Demo2 {
def main(args: Array[String]): Unit = {
Teacher.teach()
}
object Teacher {
var name = "Tom"
def teach(): Unit = {
println(name + " teach")
}
}
}
伴生对象
如果有一个class,还有一个与class同名的object,那么就称这个object是class的 伴生对象 ,class是 object的 伴生类
import scala.collection.mutable.ListBuffer
object Demo2 {
def main(args: Array[String]): Unit = {
val list = new IntList(1, 2, 3)
println(list)
val list2 = IntList(1, 2, 3)
println(list2)
}
class IntList() {
var delegate: ListBuffer[Int] = ListBuffer()
def this(nums: Int*) {
this()
for (elem <- nums) {
this.delegate.append(elem)
}
}
override def toString: String = delegate.toString
}
object IntList {
def apply(nums: Int*): IntList = {
//new IntList(nums) 这种方式不行 原因未知
val list = new IntList()
for (elem <- nums) {
list.delegate.append(elem)
}
list
}
}
}
apply是object中非常重要的一个特殊方法,通常在伴生对象中实现apply方法,并在其中实现构造伴生类对象的功能
在创建对象的时候,就不需要使用new Class的方式,而是使用Class()的方式,隐式调用伴生对象的apply方法,这样会让对象创建更加简洁
例如:Array的伴生对象的apply方法就实现了接收可变数量的参数,以及会创建一个Array对象
val a = Array(1, 2, 3, 4, 5)
接口-trait
Scala中的接口称为trait,trait类似于Java中的interface
在triat中可以定义抽象方法
类可以使用 extends 关键字继承trait,无论继承类还是trait统一都是使用 extends 这个关键字
类继承trait后,必须实现trait中的抽象方法,实现时不需要使用 override 关键字
scala不支持对类进行多继承,但是支持对trait进行多重继承,使用 with 关键字即可
class MyRunner extends Runnable with MyRunnable {
override def run(): Unit = {
println("run ...")
}
override def run2(): Unit = {
println("run2 ...")
}
}
trait Runnable {
def run(): Unit
}
trait MyRunnable {
def run2(): Unit
}
main方法
Scala中的main方法必须定义在object中,格式为 def main(args: Array[String])
函数式编程
在Scala中,函数与类、对象一样,都是一等公民,所以说scala的面向过程其实就重在针对函数的编程了,所以称之为函数式编程
object Demo3 {
def main(args: Array[String]): Unit = {
//函数赋值给变量
var mySum = sum _
println(mySum(2, 4))
//匿名函数
var mySum2 = (a: Int, b: Int) => a + b
println(mySum2(3, 6))
//函数当做参数
println(sum2(a => a * a, 1, 2, 3))
//内置高阶函数 类似java中的StreamAPI
val listSum = List(1, 2, 3)
.filter(a => a % 2 != 0) //可以简写为 _ % 2 != 0 _就表示遍历中的元素
.map(a => a * a)
.sum
println(listSum)
val listJoin = List("hello you", "hello me")
.flatMap(a => a.split(" ").toList)
.reduceLeft((a: String, b: String) => a + "-" + b)
println(listJoin)
}
def sum(a: Int, b: Int): Int = {
a + b
}
def sum2(func: Int => Int, nums: Int*): Int = {
var sum = 0
for (elem <- nums) {
sum += func(elem)
}
sum
}
}
模式匹配
模式匹配是Scala中非常有特色,非常强大的一种功能。
模式匹配,其实类似于Java中的 switch case 语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理
object Demo4 {
def main(args: Array[String]): Unit = {
//值匹配
println(week(1))
println(week(2))
println(week(3))
//类型匹配
processException(new IllegalArgumentException("error"))
processException(new IOException("error"))
processException(new Exception("error"))
//case class 匹配
println(checkPerson(Teacher("Tom")))
println(checkPerson(Student("Jack")))
println(checkPerson(new Person()))
}
def checkPerson(p: Person): String = {
p match {
case Teacher(name) => "Teacher"
case Student(name) => "Student"
case _ => "None"
}
}
class Person
case class Teacher(name: String) extends Person
case class Student(name: String) extends Person
def processException(e: Exception): Unit = {
e match {
case e1: IllegalArgumentException => println("IllegalArgumentException: " + e)
case e2: IOException => println("IOException: " + e)
case e3: Exception => println("Exception: " + e)
}
}
def week(day: Int): String = {
day match {
case 1 => "Monday"
case 2 => "Tuesday"
case _ => "None"
}
}
}
当一个类被声明为case class的时候,scala会帮助我们做下面几件事情:
- 构造器中的参数如果不被声明为var的话,它默认的话是val类型的,但一般不推荐将构造器中的参数声明为var;
- 自动创建伴生对象,同时在里面给我们实现apply方法,使得我们在使用的时候可以不直接显示地new对象;
- 伴生对象中同样会帮我们实现unapply方法,从而可以将case class应用于模式匹配;
- 实现自己的toString、hashCode、copy、equals方法。
除此之此,case class与其它普通的scala类没有区别。
隐式转换
Scala的隐式转换,允许手动指定将某种类型的对象转换成其它类型的对象
Scala的隐式转换,最核心的就是定义隐式转换函数,即implicit conversion function
隐式转换函数与普通函数唯一的语法区别是要以implicit开头而且最好要定义函数返回类型
隐式转换非常强大的一个功能,就是可以在不知不觉中加强现有类型的功能。也就是说,我们可以为某个普通类定义一个加强类,并定义对应的隐式转换函数,这样我们在使用加强类里面的方法的时候,Scala会自动进行隐式转换,把普通类转换为加强类,然后再调用加强类中的方法
object Demo5 {
def main(args: Array[String]): Unit = {
val dog = new Dog("Tom")
dog.catchMouse()
}
class Cat(name: String) {
def catchMouse() {
println(this.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
}
}
我们后续在工作中一般很少需要我们自己去定义隐式转换函数,大部分的场景是我们只需要使用import导入对应的隐式转换函数就可以了,在这个案例中我们是自己手工实现了一个隐私转换函数,因为他们都在一个作用域内,所以就不需要import了。
标签:语言,val,Scala,Int,name,println,def,入门 From: https://www.cnblogs.com/strongmore/p/17355089.html