Scala 教程
⚡ 智能摘要
Scala教程介绍了一种基于JVM的静态类型语言,它融合了面向对象编程和函数式编程的特点。本指南涵盖了安装、语法、核心特性以及各种框架,这些内容使Scala成为构建并发、可扩展且简洁的现代应用程序的理想选择。
什么是斯卡拉?
斯卡拉 Scala 是一种静态类型编程语言,它结合了面向对象和函数式编程,以提高应用程序的可扩展性。Scala 主要运行在 JVM 平台上,开发者还可以通过 Scala-Native 为原生平台编写软件,或者通过 ScalaJs 为浏览器编写软件。 JavaScript 运行。
Scala 是一种可扩展的语言,用于构建跨平台软件,这也是它名称的由来。该语言旨在解决以往代码中存在的一些冗长问题。 Java 同时保持简洁性。Scala 由 Martin Odersky 设计,第一个公开版本于 2004 年发布。
为什么要学习Scala
学习Scala编程语言的主要原因如下:
- Scala对于面向对象程序员来说很容易学习,而且 Java 开发者们,它已成为近年来流行的语言之一。
- Scala 为用户提供一流的函数。
- Scala 可以在以下平台上执行: JVM为与其他语言的互操作性铺平了道路。
- 它专为并发、分布式和弹性消息驱动型应用程序而设计,这使其成为本世纪最热门的语言之一。
- 它是一种简洁而强大的语言,可以根据用户的需求快速发展。
- 它采用面向对象编程,并包含许多函数式编程特性,使开发人员在编写代码时拥有很大的灵活性。
- Scala 提供 Duck Typing 通过结构类型。
- 它的代码量比……少。 Java.
- 用 Scala 编写的 Lift 和 Play 框架不断成熟,越来越受欢迎。
如何安装 Scala
要开始编写 Scala 程序,您需要在计算机上安装 Scala。为此,请访问官方网站: https://www.scala-lang.org/download/ 下载最新版本的Scala。
点击链接后,您将看到两种安装 Scala 的选项。在本 Scala 教程中,我们将使用第一种选项。 IntelliJ IDEA它开箱即用,提供出色的 Scala 支持。
点击下载链接后,你会发现 IntelliJ IDE 有两个版本。
在本 Scala 教程中,我们将下载社区版,它是免费的,并且包含了编写 Scala 程序所需的一切。
步骤1) 选择社区版。
在页面上,点击“社区版”下拉菜单。
它提供了一个选项,可以将 IntelliJ IDE 与 JBR 一起下载,JBR 中包含 JDK 实现(Java Scala 需要 OpenJDK 等开发工具包来编译和运行代码。
步骤2) 运行安装程序。
下载 IntelliJ 后,双击安装程序并按照屏幕上的向导操作。
步骤3) 选择一个位置。
选择安装 IDE 的位置。
如果您没有下载包含 JDK 的软件包,安装程序仍会在安装过程中提示您通过复选框添加 JDK。
步骤4) 点击下一步。
其他设置保持默认不变,然后单击“下一步”。
Scala Hello World 程序
步骤5) 点击启动图标。
安装完成后,点击开始菜单中的启动图标即可像运行普通应用程序一样运行 IntelliJ IDE。
您仍然需要将 Scala 插件添加到 IntelliJ 中。单击屏幕右下角配置菜单中的下拉菜单,然后选择“插件”选项。
在“应用市场”选项卡中,搜索 Scala。该插件会在“语言”标签下显示为第一个结果。
步骤6) 安装插件。
点击“安装”开始下载 Scala 插件。
步骤7) 重启IDE。
下载完成后,系统会提示您重新启动 IDE,以便安装的插件能够开始工作。
重启后,您将返回到之前的同一页面,但现在 Scala 插件已安装并准备就绪。
步骤8) 选择“创建项目”,这将进入一个页面,您可以在该页面上选择项目的语言。
步骤9) 选择 Scala,选中 Scala 复选框,然后单击“下一步”。
步骤10) 选择项目文件的保存位置,并为项目命名。
如果目录不存在,IntelliJ 会请求创建文件夹的权限。接受并单击“完成”。之后,您将进入 Scala 项目,该项目目前不包含任何 Scala 代码。
加载索引需要一些时间,因此如果底部显示进度条时您无法与 IDE 交互,请不要担心。IDE 正在加载运行 Scala 并提供自动补全功能所需的文件。
步骤11) 点击 IDE 左侧的“项目”选项卡,展开即可查看项目内容。
此时,项目是空的,只包含一个 .idea 文件夹和一个由 IDE 生成的 hello-world.iml 文件。我们需要关注的是 src 文件夹,它存放着项目的源代码。在这里,您可以创建您的第一个 Scala 文件。
步骤12) 右键单击 src 打开菜单并创建一个新的 Scala 文件。
为文件选择一个名称。在本 Scala 教程中,请使用 hello 然后从下拉列表中选择“对象”作为 Scala 文件的内容类型。
完成此操作后,您将得到一个包含单例对象的 Scala 文件,您将使用该对象来运行代码。
现在你已经有了一个包含 Hello 对象的 Scala 文件,请使用 App 关键字扩展你创建的对象,编写你的第一个程序。
使用 App 扩展对象会告诉编译器程序启动时要运行哪些代码。扩展 App 后,左侧会立即出现一个绿色箭头,表示程序现在可以运行了。
在 Hello 对象内部,编写 println() 函数将文本打印到控制台。点击绿色箭头运行程序。
点击箭头会显示“运行 Hello”选项。选择此选项会编译代码,几秒钟后程序输出会出现在 IntelliJ 控制台中。
您已成功安装 Scala 并运行了您的第一个程序。
Scala 可以做什么
Scala 因其灵活性和与 JVM 的互操作性而被广泛应用于众多领域。常见用例包括:
- 使用 ScalaJS 进行前端 Web 开发。
- 移动开发 Android 以及使用 Scala Native 的 iOS。
- 服务器端库,例如 HTTP4S、Akka-Http 和 Play Framework。
- 物联网(IoT)应用。
- 游戏开发。
- 使用 ScalaNLP 库套件进行自然语言处理。
- 同时练习函数式编程和面向对象编程等高级技术。
- 使用 Actor 的高并发通信应用程序,Actor 是一个受 Erlang 启发的 JVM 库。
- 利用 Figaro 等概率编程库和 Apache 等库进行机器学习。 Spark 用于大规模数据处理。
匿名函数
Scala 语言支持匿名函数,也称为匿名函数。 函数文字由于 Scala 是一种函数式语言,开发者通常会将大型问题分解成许多小型任务,并创建许多函数。为了简化这一过程,Scala 允许函数…… 实例化时无需名称你可以直接将它们分配给变量或定义,使用以下方法: def如下面的 Scala 示例所示:
val multiplyByTwo = (n:Int) => n * 2 def multiplyByThree = (n:Int) => n * 3
然后,您可以通过传递参数来像使用普通函数一样使用它们:
multiplyByTwo(3) //6 multiplyByThree(4) //12
当你需要编写简洁清晰的代码时,这些方法就派上用场了。匿名函数适用于定义小型且函数体代码量不大的方法。它们非常简单,创建起来也无需任何繁琐的步骤。
这些方法不仅限于带参数的函数,还可以用来实例化不带参数的方法。
val sayHello = () => { println("hello") }
大多数匿名函数用于代码的其他部分,以便在需要快速实现某个功能时使用。因此,它们也被称为…… 内联函数使用匿名函数是集合库中常见的一种模式,用于对集合执行快速操作。
例如,filter 方法接受一个内联函数,以创建另一个集合,其中只包含符合匿名函数中定义的条件的元素。
val myList = List(1,2,3,4,5,6,7) val myEvenList = myList.filter((n: Int) => n % 2 == 0) //List(2,4,6) val myOddList = myList.filter((n:Int) => n % 2 != 0) //List(1,3,5,7)
这里,匿名函数检查列表中的值是奇数还是偶数,并返回匹配的项。
//the one checking that the value is even (n: Int) => n % 2 == 0 //the one checking that the value is odd (n:Int) => n % 2 != 0
在 Scala 中,也可以使用通配符来指定未命名的匿名函数参数。例如:
var timesTwo = (_:Int) * 2 timesTwo(5) //10
在这种情况下,您无需为参数命名。下划线用于表示参数。
懒惰评估
大多数语言会按顺序依次求值变量和函数参数。在 Scala 中,关键字 lazy 有助于处理那些在被引用之前不想对其进行评估的值。
标记为惰性求值的变量在其定义处不会立即求值,这通常被称为即刻求值。它仅在代码后续引用时才会被求值。
当计算某个值需要耗费大量资源时,这种方法非常有用。如果并非总是需要该值,那么通过将变量设置为惰性变量,就可以避免执行耗时的计算,从而防止软件运行速度变慢。
lazy val myExpensiveValue = expensiveComputation def runMethod() = { if(settings == true) { use(myExpensiveValue) } else { use(otherValue) } }
这并非惰性变量的唯一用途。它们还能帮助处理代码中的循环依赖问题。
如果设置标志为 false,则无需使用 myExpensiveValue,从而避免执行耗时的计算。这有助于确保用户获得流畅的体验,因为不会将资源浪费在永远不会读取的值上。
惰性求值也适用于函数参数,参数仅在函数内部被引用时才使用。这种概念称为按名称调用参数。
def sometimesUsedString(someValue:String, defaultValue: => String) = { if(someValue != null) { use(defaultValue) } else { use(someValue) } }
许多语言采用按值调用的方式来评估参数。通过按名称调用传递的参数仅在函数体内部需要时才进行评估。一旦评估完成,该值就会被存储起来,以后无需重新评估即可重用,这种概念称为记忆化。
类型推断
在 Scala 中,你无需为创建的每个变量声明类型,因为 Scala 编译器可以从表达式的右侧推断类型。这使得你的代码更加简洁,并避免了在类型显而易见的情况下编写冗余代码。
var first:String = "Hello, " var second:String = "World" var third = first + second //the compiler infers that third is of type String
高阶函数
高阶函数是指可以接受函数作为参数,并且可以返回函数作为返回值的函数。在 Scala 中,函数被视为一等公民。这种函数使用方式使你能够非常灵活地构建各种类型的程序。你可以动态地创建函数,并动态地向其他函数传递功能。
def doMathToInt(n:Int, myMathFunction:Int=>Int): Int = { myMathFunction(n) }
在上面的函数中,你传入一个 Int 以及一个接受参数的函数 Int 并返回一个 Int你可以传入任何具有该签名的函数。签名指的是函数的输入和输出。一个函数的签名是: Int => Int 表示该函数接受一个整数作为输入,并返回一个整数作为输出。
签名 () => Int 这意味着该函数不接受任何输入,并返回一个整数作为输出。例如,生成随机整数的函数就是这样的。
def generateRandomInt() = { return scala.util.Random.nextInt() }
上述函数具有签名 () => Int.
函数也可以有签名。 () => Unit这意味着该函数不接受任何参数,也不返回任何类型。该函数可以改变某些状态或执行预定的操作。
不建议采用这类方法,因为它们就像一个黑箱,会以未知的方式影响系统。而且,它们也更难测试。明确定义输入和输出类型,能让你更好地理解函数的运行机制。
高阶函数也可以返回一个函数。例如,你可以创建一个方法,该方法返回一个幂函数,该函数接受一个数字并对其应用幂运算。
def powerByFunction(n:Int): Int=>Int = { return (x:Int) => scala.math.pow(x,n).toInt }
上面的函数接受一个整数 (Int) 类型的参数。它的返回值是一个接受整数 (Int) 类型的匿名函数。 x 并将其用作幂函数的参数。
柯里化
在 Scala 中,你可以将一个接受两个参数的函数转换为一个每次只接受一个参数的函数。当你传入一个参数时,你会先部分应用它,然后函数会接受剩余的参数来完成调用。柯里化允许你通过部分添加参数来构建函数。
这对于在拥有完整参数集之前动态创建函数非常有用。
def multiplyTwoNumbers(n:Int)(m:Int): Int = { return n * m }
如果你需要一个乘以特定数字的函数,无需创建另一个乘法方法。你可以将一个参数传递给柯里化函数并重用结果:
def multiplyTwoNumbers(n:Int)(m:Int): Int = { return n * m } var multiplyByFive = multiplyTwoNumbers(5) _ multiplyByFive(4) //returns 20
模式匹配
Scala 有一个强大的内置机制,可以帮助你检查变量是否符合特定条件,很像 JavaScript 中的 switch 语句。 Java 或者一系列 if/else 语句。该语言支持模式匹配,可用于检查变量是否属于特定类型。Scala 中的模式匹配功能强大,可以解构具有特定类型的组件。 unapply 直接从匹配的变量中获取您关心的字段的方法。
Scala 模式匹配的语法也比 switch 语句更简洁易懂。
myItem match { case true => //do something case false => //do something else case _ => //if none of the above do this by default }
您将变量与一组选项进行比较。当变量符合条件时,粗箭头(=>)右侧的表达式将被计算并返回匹配结果。
下划线用于捕获代码中未匹配的情况,类似于 switch 语句中 default 的情况的行为。
class Animal(var legs:Int, var sound:String) class Furniture(var legs:Int, var color:Int, var woodType:String) myItem match { case myItem:Animal => //do something case myItem:Furniture => //do something else case _ => //unknown type, do something else }
在上面的代码中,您可以检测出类型 myItem 根据变量,跳转到特定代码。
模式匹配检查变量是否按顺序与每个情况匹配。
下划线用作占位符,匹配其他 case 语句未涵盖的任何条件。您需要一个变量。 myItem 并致电 match 方法。
- 检查是否
myItemistrue并执行粗箭头“=>”右侧的逻辑。 - 使用下划线来匹配代码中定义的任何 case 语句都不匹配的内容。
使用 case 类,你可以解构类来执行示例操作。trac对象内部的 t 字段。
通过使用 sealed 使用关键字定义类的好处是,编译器会彻底检查你尝试匹配的情况,并在你忘记处理某个情况时发出警告。
不变性
在 Scala 中,可以使用以下方法创建其他函数无法更改的值: val 关键词。这是通过以下方式实现的: Java 通过使用 final 关键字。在 Scala 中,您可以使用一个 val 创建变量时,而不是 var这是可变变量的替代方案。
使用以下方式定义的变量 val 关键字是只读的,而用以下方式定义的关键字是可读的: var 之后可以由其他函数或用户在代码中读取和更改。
var changeableVariable = 8 changeableVariable = 10 //the compiler does not complain, and the code compiles successfully println(changeableVariable) //10 val myNumber = 7 myNumber = 4 //this will not compile
尝试给……赋值 myNumber 在宣布它为之后 val 抛出编译时错误:“重新赋值给 val”。
为什么要使用不可变性?
不可变性有助于防止代码和其他程序员意外地更改值,从而避免不可预测的结果。如果值的使用者需要更改它,他们可以创建一个副本。这样,就可以防止因多个参与者更改同一变量而导致的错误。
类和对象
对象是现实世界中的实体,而类是定义对象的模板。类既有状态也有行为。状态可以是值或变量。行为则是 Scala 中的方法。
下一节将介绍如何在 Scala 中定义类、实例化类和使用类。
这里有一个名为 Rectangle 的类,它有两个值和两个函数。您也可以使用参数。 l 和 b 直接作为程序中的字段。你有一个对象,它有一个 main 方法,并使用两个值实例化该类。
计费示例:
class Rectangle(l: Int, b: Int) { val length: Int = l val breadth: Int = b def getArea: Int = l * b override def toString = s"This is a rectangle with length $length and breadth $breadth" } object RectObject { def main(args: Array[String]) { val rect = new Rectangle(4, 5) println(rect.toString) println(rect.getArea) } }
在 Scala 中,所有字段和方法默认都是公共的。使用起来至关重要。 override 因为 toString 在 Scala 中,每个对象都已定义。
遗产
Scala 拥有多种继承类型(单继承、多级继承、多重继承、层次继承和混合继承),这些类型与传统继承形式有很多共同之处。 Java你可以从类和特性中继承。使用关键字可以将一个类的成员继承到另一个类中。 extends这样就实现了可重复使用。
可以从一个或多个类继承。也可以从本身拥有超类的子类继承,从而形成继承层次结构。
在下面的 Scala 示例中,基类是 Circle,派生类是 Sphere圆有一个称为半径的值,该值由 Sphere 类继承。 calcArea 可通过关键字进行覆盖 override.
计费示例:
class Circle { val radius = 5 def calcArea = { println(radius * radius) } } class Sphere extends Circle { override def calcArea = { println(radius * radius * radius) } } object SphereObject { def main(args : Array[String]) { new Sphere().calcArea } }
ABStracTION
在 Scala 中,你可以创建绝对值trac使用 abs 的 t 方法和成员字段tract 类和特征。内部 abstract 类和特征,您可以定义绝对值tract 字段,但未实现它们。
计费示例:
trait MakesSound { var nameOfSound:String def sound():String } abstract class HasLegs(var legs:Int) { val creatureName:String def printLegs():String = { return s"$creatureName has this number of legs: $legs" } }
这些字段由继承自 trait 或 abs 的类实现。tract 类。您可以使用特性来创建 contrac先定义应用程序应该做什么,然后再实现这些方法。
trait DatabaseService { def addItemName(itemName:String) def removeItem(itemId:Int) def updateItem(itemId:Int, newItemName:String) }
这样,你就可以在不实现方法的情况下规划应用程序的外观,这很有帮助。ping 你设想各种方法将如何呈现。它遵循一种被称为“编程到绝对值”的模式。trac重点在于方案,而不是实际执行。
以……开头的课程 abstract 关键词可以包含两者tract 和非绝对tract 方法。然而,abs 不支持多重继承。tract 类,所以你只能扩展一个 abstract 类。
单例对象
单例模式是指程序中只实例化一次的类。它源自一种流行且实用的编程模式,称为“单例模式”。单例模式适用于创建需要长期运行且在程序中频繁访问的实例,尤其适用于状态对于协调系统事件至关重要的场景。在 Scala 中创建这样的类非常容易,因为 Scala 提供了一种使用 `__init__` 语句创建单例的简单方法。 object 关键词。
object UserProfile { var userName = "" var isLoggedIn:Boolean = false }
然后,您可以在整个程序中引用此对象,并保证程序的所有部分都能看到相同的数据,因为它只有一个实例。
def getLoggedInStatus():Boolean = { return UserProfile.isLoggedIn } def changeLoggedInStatus():Boolean = { UserProfile.isLoggedIn = !UserProfile.isLoggedIn return UserProfile.isLoggedIn }
Scala 中不存在静态成员的概念,因此需要使用单例对象,它们的作用类似于类的静态成员。
隐式类
隐式类是 2.10 版本中新增的一项功能,主要用于向现有的封闭类添加新功能。
此 implicit 关键字应该在类、对象或特性中定义。隐式类的主构造函数必须在其第一个参数列表中恰好包含一个参数。它还可以包含一个额外的隐式参数列表。
在下面的 Scala 示例中,新增了将字符串中的元音字母替换为 * 的功能。
object StringUtil { implicit class StringEnhancer(str: String) { def replaceVowelWithStar: String = str.replaceAll("[aeiou]", "*") } }
你需要将隐式类导入到你使用该类的类中。
import StringUtil.StringEnhancer object ImplicitEx extends App { val msg = "This is Guru99!" println(msg.replaceVowelWithStar) }
面向对象编程 (OOP) 与函数式编程 (FP)
在面向对象编程中,程序是由一组对象构成的。ping 数据以及操作这些数据的函数被整合为高度关联的单元。对象通过字段存储数据,方法则直接操作这些字段。在这种编程风格中,主要的抽象概念是……trac关键在于数据,因为创建的方法是为了对数据进行操作。
功能编程另一方面,它将数据与操作数据的函数分离。这使得开发人员能够将函数视为绝对值。trac建模程序时的作用和驱动力。
Scala 通过将函数视为一等公民来实现函数式编程,允许函数作为值传递给其他函数,也可以作为值返回。这两种编程范式的结合使 Scala 成为构建数据科学等行业复杂软件的理想选择。
Scala 上的重要框架
以下是一些重要的Scala框架:
- 下注 是一个开源 Web 应用程序框架,它使用 MVC架构该框架于 2007 年发布,现采用 Apache 许可证,到 2013 年已成为 GitHub 上最受欢迎的框架之一。LinkedIn 等公司都在使用它。 Walmart三星和 Eero 都使用了这种框架。
- 电梯 Lift 是另一个用 Scala 编写的免费 Web 框架,于 2007 年发布。Foursquare 使用的是 Lift 框架。它是一个高性能且构建速度快的框架。
- 阿卡 提供一套工具包,用于在 JVM 上构建高度并发、分布式和弹性的消息驱动应用程序。
- 猫 是一个提供绝对值的库。trac函数式编程的指导原则。
- Spark 是一个用于大规模数据处理的统一分析引擎,广泛应用于大数据和机器学习工作流程。
并发支持
- Scala 中的值默认是不可变的,这使得它能够很好地适应并发环境。
- Scala 的许多特性使其成为并发应用程序的理想选择。
- Futures 和 Promises 使得异步处理数据变得更容易,从而支持并行性。
- Akka 是一个使用 Actor 并发模型的工具包。Actor 在收到消息时执行操作。
- 使用来自的线程进行并发 Java Scala 也支持此功能。
- 流处理是另一项强大的功能,它能够实现数据的持续实时处理。
Scala 拥有一些最好的并发库 Java 生态系统:
- 本地人 Java 线程。
- 来自 Vertx 等库的 Fibers。
- ZIO 是一个包含用于处理并发和异步计算的基本功能的库。
- 用于事务处理的软件事务存储器(STM)。
- Future 是 Scala 语言内置的功能。
Java 与 Scala 相比
这是主要的 之间的差异 Java 和 Scala.
| 斯卡拉 | Java |
|---|---|
| 更紧凑、更简洁。 | 相对较大的代码块。 |
| 设计和开发兼顾面向对象和函数式编程。支持多种函数式编程特性,例如并发性和不可变性。 | 最初开发时是一种面向对象的语言,最近几个版本才开始支持函数式编程特性。但它在函数式编程方面仍然不如Scala强大。 |
| 使用 Actor 模型来支持并发。 | 使用传统的基于线程的模型进行并发。 |
| 支持 Play 和 Lift 等框架。 | 支持 Spring、Grails 等多种框架。 |
| 支持惰性求值。 | 本身不支持惰性求值。 |
| 没有静态成员。 | 包含静态成员。 |
| 支持运算符过载。 | 不支持运算符重载。 |
| 源代码编译速度相对较慢。 | 源代码编译速度比 Scala 快。 |
| 特质的作用就像 Java 8 个接口。 | Java 8 个接口试图弥合类和接口之间的差距。 |
| 版本升级时有时需要重写代码。 | 版本之间通常不需要重写代码。 |
| 类型安全提供了强大的代码可靠性,但并不能绝对保证没有错误。 | 强静电型ping 有助于减少许多运行时缺陷。 |
| 在主要版本之间存在一些向后兼容性限制。 | 强大的长期向后兼容性。 |
| Operators 被视为方法调用。 | Operators 是特殊语法,不是方法调用。 |
| 支持使用特性进行多重继承。 | 不支持使用类进行多重继承;而是使用接口。 |
| Code 以简洁的形式写成。 | Code 写得比较冗长。 |
| Scala 不包含 static 关键字。 | Java 包含 static 关键字。 |























