Scala 教程:Scala 编程语言示例和代码

Scala 教程摘要

本 Scala 教程涵盖了 Scala 的所有方面和主题。您将从头开始学习所有基础知识,例如什么是 Scala、Scala 的安装过程、Scala 程序、Scala 函数、惰性求值、类型接口、类和对象、继承、抽象、 Java 和 scala 差异等等

什么是斯卡拉?

斯卡拉 是一种静态类型编程语言,结合了函数式和面向对象编程,以提高应用程序的可扩展性。Scala 主要在 JVM 平台上运行,也可以使用 Scala-Native 为本机平台编写软件, JavaScript 通过 ScalaJs 运行。

Scala 是一种可扩展语言,用于为多个平台编写软件。因此,它得名“Scala”。该语言旨在解决以下问题: Java 同时更加简洁。它最初由 Martin Odersky 设计,于 2003 年发布。

为什么要学习Scala

以下是学习 Scala 编程语言的主要原因:

  • Scala 对于面向对象程序员来说很容易学习, Java 开发人员。它正在成为近年来流行的语言之一。
  • Scala 为用户提供一流的功能
  • Scala 可以在 JVM,从而为与其他语言的互操作铺平了道路。
  • 它专为并发、分布式、弹性消息驱动的应用程序而设计。它是近十年来要求最高的语言之一。
  • 它是一种简洁、强大的语言,并且可以根据用户的需求快速成长。
  • 它是面向对象的,具有许多函数式编程特性,为开发人员以他们想要的方式进行编码提供了很大的灵活性。
  • Scala 提供了许多 Duck 类型
  • 如果你来自 Java
  • 用Scala编写的框架Lift和Play正处于增长曲线。

如何安装 Scala

要开始编写 Scala 程序,你需要在计算机上安装它。为此,你需要访问他们的网站 https://www.scala-lang.org/download/ 以便下载最新版本的Scala。

点击链接后,我们会看到两个选项,可以选择在我们的机器上安装 Scala。在本 Scala 教程中,我们将下载 IntelliJ IDEA.

如何安装 Scala

访问下载链接后,您会发现两个版本的 IntelliJ IDE。

对于本 Scala 教程,我们将下载社区版,它是免费的,并附带编写 Scala 程序所需的一切。

如何安装 Scala

步骤1) 选择社区版
在页面上,单击社区版的下拉菜单。

它为我们提供了一个选项,可以下载 IntelliJ IDE 和包含 JDK 实现的 JBR(Java Scala 编译和运行代码所需的(开发工具包)OpenJDK。

如何安装 Scala

步骤2) 运行安装
下载 IntelliJ 后,双击它以运行安装向导并按照对话框进行操作。

如何安装 Scala

步骤3) 选择一个位置
选择安装 IDE 的位置。

如何安装 Scala

如果您碰巧没有下载带有 JDK 的版本,我们仍然会收到提示,我们可以通过选中复选框来检查是否下载它。

如何安装 Scala

步骤4) 点击下一步
保留其他默认设置并单击“下一步”。

如何安装 Scala

步骤5) 点击启动图标
安装完成后,像常规应用程序一样,单击启动菜单中的启动图标来运行 IntelliJ IDE。

如何安装 Scala

您仍然需要执行额外的步骤将 Scala 插件添加到 IntelliJ;您可以通过单击位于屏幕右下角的配置菜单上的下拉菜单并选择插件选项来完成此操作。

如何安装 Scala

如何安装 Scala

在“市场”选项卡上,搜索 Scala 将会把该插件显示为“语言”标签下的第一个结果。

步骤6) 安装插件
单击“安装”,插件将开始下载。

如何安装 Scala

步骤7) 重启 IDE
下载完成后,系统将提示您重新启动 IDE,以便安装的插件可以开始工作。

如何安装 Scala

重新启动后,你会发现自己处于与之前运行 IDE 时相同的页面,但这一次我们已经安装了 Scala 插件。

Scala Hello World 程序

步骤1) 选择创建项目选项,这将带我们进入一个页面,我们可以在其中选择项目将使用的语言类型。

Scala Hello World 程序

步骤2) 通过选中 Scala 复选框来选择 Scala,然后单击下一步。

Scala Hello World 程序

步骤3) 选择一个位置来保存我们的项目文件并为我们的项目命名。

Scala Hello World 程序

如果目录不存在,IntelliJ 将提示我们请求创建文件夹的权限。接受并单击完成。您将进入 Scala 项目,该项目目前没有任何 Scala 代码。

加载一些索引需要一些时间,所以如果您无法立即执行任何操作,请不要担心,同时 IDE 底部有一个进度条,这仅意味着您的 IDE 正在加载运行 Scala 所需的一些文件并帮助 IDE 自动完成。

步骤4) 接下来,我们点击 IDE 左侧的项目选项卡并展开,以便我们可以看到项目的内容。

Scala Hello World 程序

目前,该项目是空的,仅包含 .idea 文件夹和 IDE 生成的 hello-world.iml 文件。我们感兴趣的是 src 文件夹。Src 是我们存储项目源代码的地方。我们将在这里创建第一个 Scala 文件。

步骤5) 右键单击 src 打开菜单以创建一个新的 Scala 文件。

创建新的 Scala 文件

然后,我们将为文件创建一个名称,在本 Scala 教程中,我们将使用 hello,然后从下拉列表中选择要作为 Scala 文件内容的内容。选择“对象”

Scala Hello World 程序

一旦我们完成此操作,我们将拥有一个包含 Singleton 对象的 Scala 文件,我们将使用它来运行我们的代码。

Scala Hello World 程序

现在您有一个包含 Hello 对象的 Scala 文件。您将通过使用 App 关键字扩展您创建的对象来编写您的第一个程序。

使用 App 扩展我们的对象会告诉编译器在启动程序时要运行哪些代码。扩展 App 后,左侧会立即出现一个绿色箭头,表示您现在可以运行程序了。

Scala Hello World 程序

Scala Hello World 程序

在 Hello 对象内部,我们编写一个函数 println(),用于将其中的文本打印到控制台。我们将通过单击绿色箭头来运行代码。

单击箭头会出现运行选项,你好,单击它后,我们的代码将开始编译,几秒钟后,我们将看到从 IntelliJ IDE 内置控制台打印的程序结果。

Scala Hello World 程序

到这里,我们已经成功安装了 Scala 并运行了我们的第一个程序。

Scala 可以做什么

  • 使用 ScalaJS 进行前端 Web 开发
  • 移动开发,两者皆可 Android 开发和 IOS – 使用 Scala Native
  • 服务器端库,例如 HTTP4S、Akka-Http、Play Framework
  • 物联网利用
  • 游戏开发
  • NLP – 使用 ScalaNLP 库套件进行自然语言处理
  • 测试高级编程技术,如函数式编程和面向对象编程
  • 使用 actors 构建高度并发的通信应用程序,这是受 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") }

这些匿名函数大部分都用在我们的代码的其他部分,我们需要在这些部分创建一个快速函数。

这些函数也被称为 内联函数。使用匿名函数是一种常见模式,它在集合库中广泛用于对集合执行快速操作。

例如,我们有一个过滤方法,它采用内联函数/匿名函数来创建另一个集合,其中仅包含符合我们在匿名函数中定义的条件的元素。

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,这可以让我们免于进行昂贵的计算,从而有助于确保用户能够愉快地使用我们的应用程序,因为我们可以正确计算他们的其他需求而不会占用过多的 RAM。

如果设置为 false,我们可能不需要使用 myExpensiveValue,这可以让我们免于进行昂贵的计算,从而有助于确保用户愉快地使用我们的应用程序,因为我们可以适当地计算他们的其他需求而不会占用过多的 RAM。

惰性还有助于函数参数,其中参数仅在函数内部引用时才使用。这个概念称为按名称调用参数。

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 compile infers that third is of type String

高阶函数

高阶函数是一种可以将函数作为参数并可以返回函数作为返回类型的函数。在 Scala 中,函数被视为一等公民。以这种方式使用这些函数使我们能够非常灵活地编写程序。我们可以动态创建函数,并将功能动态地提供给其他函数。

def doMathToInt(n:Int, myMathFunction:Int=>Int): Int ={
    myMathFunction(n)
}

在上面的函数中,我们传入一个 int 和一个接受 int 并返回 int 的函数。我们可以传入具有该签名的任何函数。签名指的是函数的输入和输出。Int=>Int 的签名意味着函数接受 Int 作为输入并返回 Int 作为输出。

签名 ()=>Int 表示函数不接受任何输入,并返回一个 Int 作为输出。此类函数的一个例子是为我们生成随机 int 的函数。

def generateRandomInt()={
 return scala.util.Random.nextInt()
}

上述函数有一个签名 ()=>Int

我们可以有一个带有签名 scala ()=>Unit 的函数。这意味着函数不接受任何内容,也不返回任何类型。该函数可以通过将某事更改为执行预先确定的事情来执行某种计算。

不过,我们不鼓励使用这些方法,因为它们似乎是一个黑匣子,可能会以某些未知的方式影响系统。它们也是不可测试的。有了明确的输入和输出类型,我们就可以推断出我们的函数的作用。

高阶函数也可以返回一个函数。

例如,我们可以创建一种方法来创建一个幂函数,即获取一个数字并对其施加幂。

def powerByFunction(n:Int):Int=>Int = {
  return (x:Int)=> scala.math.pow(x,n).toInt
}

上述函数接受一个 int。我们的返回类型是一个接受 Int x 的匿名函数,* 我们使用 int x 作为 power 函数的参数。

柯里化

在 Scala 中,我们可以将一个接受两个参数的函数转换为一个每次接受一个参数的函数。当我们传入一个参数时,我们会部分应用它,最终得到一个接受一个参数的函数来完成该函数。柯里化使我们能够通过部分添加一些参数来创建函数。

这对于在拥有完整的参数集之前动态创建函数很有用

def multiply two numbers(n:Int)(m:Int): Unit ={
  return n * m
}

如果我们需要创建一个乘以某个特定数字的函数,我们不需要创建另一种乘法方法。

我们可以简单地在上面的函数上调用 .curried,得到一个先接受一个参数的函数,然后返回一个部分应用的函数

def multiplyTwoNumbers(n:Int)(m:Int): Unit ={
  return n * m
}

var multiplyByFive = multiplyTwoNumbers(5) 

multiplyByFive(4)

//returns 20

模式匹配

Scala 有一个强大的内置机制来帮助我们检查一个变量是否符合特定的标准,就像我们在 switch 语句中所做的那样 Java 或一系列 if/else 语句。该语言具有模式匹配功能,我们可以使用它来检查变量是否属于特定类型。Scala 中的模式匹配功能非常强大,可用于解构具有 unapply 方法的组件,以便直接从匹配的变量中获取我们感兴趣的字段。

与 switch 语句相比,Scala 的模式匹配还提供了更令人愉悦的语法。

myItem match {
  case true => //do something
  case false => //do something else
  case  _ => //if none of the above do this by default
}

我们将变量与一组选项进行比较,当匹配的变量满足条件时,粗箭头(=>)右侧的表达式将进行评估并作为匹配结果返回。

我们使用下划线来捕获代码中不匹配的情况。它反映了处理 switch 语句时默认情况的行为。

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 _ => //case we have a type we don't recognize do sth else
}

在上面的代码中,您能够找出 myItem 变量的类型,并基于该类型分支到某些特定的代码。

模式匹配检查变量是否匹配

下划线充当占位符,用于匹配上述 case 语句中其他项目未匹配的任何其他条件。我们采用变量 myItem 并调用 match 方法。

  • 我们使用检查 myItem 是否为真,并在粗箭头“=>”的右侧执行一些逻辑操作。
  • 我们使用下划线来匹配任何与我们在代码中定义的任何 case 语句不匹配的内容。

使用 Case 类,我们甚至可以更进一步解构类以获取对象内部的字段。

通过使用 sealed 关键字来定义我们的类,我们可以让编译器详尽地检查我们尝试匹配的情况,并在我们忘记处理特定情况时发出警告。

不变性

在 Scala 中,可以使用 val 关键字创建不能被其他函数更改的值。这是通过以下方式实现的 Java 通过使用 final 关键字。在 Scala 中,我们在创建变量时使用 val 关键字,而不是使用 var,后者是我们创建可变变量的替代方法。

使用 val 关键字定义的变量是只读的,而使用 var 定义的变量可以被其他函数或用户在代码中任意读取和更改。

var changeableVariable = 8

changeableVariable =10
//the compiler doesn't complain, and the code compiles successfully

println(changeableVariable)
//10

val myNumber = 7

myNumber = 4

//if we try this the code won't 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 rectangle with length as $length and breadth as  $breadth"
  }
object RectObject {
  def main(args: Array[String]) {
    val rect = new Rectangle(4, 5)
    println(rect.toString)
    println(rect.getArea)    
  }
}

Scala 中所有字段和方法默认都是公共的。必须使用 override,因为 Scala 中为 Object 定义了 toString 方法。

遗产

Scala 有多种类型的继承(如单继承、多继承、多继承、分层继承、混合继承),它们与传统的继承形式有很多共同之处。 Java。您可以从类和特征继承。您可以使用关键字“extends”将一个类的成员继承到另一个类中。这实现了可重用性。

可以从一个或多个类继承。也可以从具有超类的子类继承,从而在此过程中创建继承层次结构。

在下面的 Scala 示例中,基类是 Circle,派生类是 Sphere。圆有一个名为 radius 的值,该值在 Sphere 类中继承。使用关键字 override 覆盖方法 calcArea。

示例:

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 
    }
  }

抽象化

在 Scala 中,我们可以使用抽象类和特征创建抽象方法和成员字段。在抽象类和特征中,我们可以定义抽象字段而不必实现它们。

示例:

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 DatabaseService{
    def addItemName(itemName:String)
    def removeItem(itemId:Int)
    def updateItem(itemId:Int, newItemName:String)
}

这样,我们就可以规划出我们的应用程序是什么样子,而无需实现这些方法,这可以帮助我们设想各种方法是什么样子。它遵循一种称为面向抽象编程而不是实际实现的模式。

以关键字 abstract 开头的类可以包含抽象方法和非抽象方法。但是,抽象类不支持多重继承。因此,您最多可以扩展一个抽象类。

单例对象

单例是程序中只实例化一次的类。它来自一种流行且有用的编程模式,称为“单例模式”。它在创建旨在长期存在且将在整个程序中被普遍访问的实例时很有用,其状态对于协调系统事件至关重要。在 Scala 中创建这样的类很容易,因为 Scala 为我们提供了一种使用 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.1版本之后新增的功能,主要是给封闭的类添加新功能。

隐式关键字应在类、对象或特征中定义。隐式类的主构造函数在其第一个参数列表中应只有一个参数。它还可以包含一个额外的隐式参数列表。

在下面的 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)

在 OOP 中,程序是通过将数据和对数据进行操作的函数分组到高度连接的单元中来构建的。对象在字段和对其操作的方法中携带数据。在这种编程风格中,主要的抽象是数据,因为创建的方法旨在对数据进行操作。

功能编程而另一方面,它将数据和操作数据的函数分离开来,使得开发人员在对程序进行建模时,可以将函数作为抽象和驱动力。

Scala 通过将函数作为一等公民来实现函数式编程,允许它们作为值传递给其他函数并作为值返回。这两种范式的结合使 Scala 成为在数据科学等各个行业中构建复杂软件的绝佳选择。

Scala 上的重要框架

以下是 Scala 的一些重要框架

  • 下注 是一个开源 Web 应用程序框架,它使用 MVC架构。2007年发布,现在获得Apache许可,2013年成为GitHub上最受欢迎的框架。LinkedIn、Walmart、Samsung、Eero等公司都使用该框架。
  • 电梯 是另一个用 Scala 编写的免费 Web 框架,于 2007 年推出。Foursquare 使用 Lift 框架。它性能高,构建框架速度更快。
  • 阿卡
  • Spark

并发支持

  • Scala 中的值默认是不可变的。这使其非常适应并发环境。
  • Scala 的许多特性使其最适合并发应用程序。
  • Futures 和 Promises 使得异步处理数据变得更容易,从而支持并行性。
  • Akka – 使用 Actor 并发模型的工具包。有许多 Actor 在收到消息时采取行动。
  • 使用来自的线程进行并发 Java Scala 中也可以支持。
  • 流处理是另一个重要的特性,它能够持续、实时地处理数据。

Scala 拥有一些最好的并发库 Java 生态系统。

  • 本地人 Java 线程
  • 来自 Vertex 等库的光纤
  • ZIO – 一个具有原语的库,可以帮助我们处理并发和异步计算
  • STM——交易
  • 未来——内置于 Scala 语言中

Java 与 Scala 相比

这是主要的 之间的差异 Java 和 Scala.

斯卡拉 Java
更加紧凑简洁 相对较大的代码块
设计和开发为面向对象和面向功能的语言。
支持多种函数式编程特性,如并发性、不变性。
最初是作为面向对象语言开发的,最近开始支持函数式编程特性。作为函数式编程语言,它仍然不够强大。
使用现代的参与者模型来支持并发 使用传统的基于线程的模型进行并发。
支持框架——Play、Lift 支持 Spring、Grails 等
支持惰性求值 不支持惰性求值
没有静态成员 包含静态成员
支持运算符重载 不支持运算符重载
源代码编译相对较慢 源代码编译速度比 Scala 更快
特征——表现得像 Java 8个接口 Java 8 接口试图弥合类和接口之间的差距
需要重写 无需重写
无法保证代码无缺陷 完全保证较小的缺陷
支持向后兼容。 Scala 不支持向后兼容。
Opera受到不同对待 Java 并且不是方法调用。 所有对条目的操作都是通过 Scala 中调用的方法进行的。
支持使用类的多重继承,但不支持抽象类的多重继承 不支持使用类的多重继承,但支持通过接口
代码以紧凑的形式编写。 代码是以长格式编写的。
Scala 不包含 static 关键字。 Java 包含 static 关键字。

总结

在本教程中,您学习了如何开始使用 Scala。您还学习了函数式和面向对象特性。您还发现了 Scala 和 Scala 之间的相似之处和不同之处。 Java 和 Scala。本教程应该已经为您提供了各种各样经过良好演示的示例。