Scala基础语法

Scala-Hello World

目标,在本地启动一个项目,能够输出helloworld

https://www.scala-lang.org/

官网提供的下载安装方式有很多种。

推荐IDE: IDEA 和 VSCode

这里我安装了IDEA,就用了它。

  1. 创建scala文件的时候,提示安装Scala插件,点击安装
  2. 写完helloworld的时候提示没有Scala SDK,点击安装。

创建项目的时候使用 sbt(Scala专用构建工具)

![image-20210621154421590](/Users/erwa/Library/Application Support/typora-user-images/image-20210621154421590.png)

object HelloWorld {
  /* 这是我的第一个 Scala 程序
   * 以下程序将输出'Hello World!'
   */
  def main(args: Array[String]) {
    println("Hello, world!") // 输出 Hello World
    print(args.toString)
  }
}

基础语法

1、两种类型的变量:val和 var

var : 创建一个可变变量

val: 创建一个不可变的变量

2、如果你想,你可以像这样将它们附加在一起:(字符串插值)

val name = firstName + " " + mi + " " + lastName

但是,Scala 提供了这种更方便的形式:

val name = s"$firstName $mi $lastName"

但是,Scala 提供了这种更方便的形式:

val name = s"$firstName $mi $lastName"

3、Scala 分号可以不写

4、Scala代码块

方法和变量定义可以是如下的单行:

def meth() = "Hello World"

变量定义也可以是代码块。

val x3:String= {
 val d = new java.util.Date()
 d.toString()
}

Schal数据类型

1、Scala类型层次结构

与Java不同,Scala中没有原生类型。

Scala中的所有数据类型都是具有对其数据操作的方法的对象。

所有Scala类型作为类型层次结构的一部分存在。

您在Scala中定义的每个类也将自动属于此层次结构。

Any
+---AnyVAl
|     +---Numberic Types
|     |
|     +---Char
|     |
|     +---Boolean
|
+---AnyRef
    +---Collections
    |
    +---Classes
    |     +---Null
    |
    +---String

Any,AnyVal和AnyRef类型

Any是Scala类层次结构的根,是一个抽象类。

Scala中的每个类都直接或间接从这个类继承。

AnyVal和AnyRef扩展任何类型。 Any,AnyVal和AnyRef类型是Scala类型层次结构的根。

所有其他类型都来自AnyVal和AnyRef。

扩展AnyVal的类型称为值类型。

2、布尔类型

val x = !false 

3、字符类型

val x = 'X'

4、字符串类型

val hello = "Hello" 

字符串插值

字符串插值是一种将字符串中的值与变量组合的机制。

Scala中的插值符号是在字符串的第一个双引号之前添加的s前缀。

然后可以使用美元符号运算符$引用变量。

以下代码说明了字符串插值的用法。

object Main {def main(args: Array[String]) {   val bookTitle = "Scala" // creating a String    // String interpolation    println(s"Book Title is ${ bookTitle}" );}}

5、数字类型

数据类型 描述
Byte 从-128到127范围内的整数
Short 从-32768到32767范围内的整数
Int 从-2147483648到2147483647范围内的整数
Long 从-9223372036854775808到9223372036854775807范围内的整数
Float 最大正有限浮点是3.4028235 * 1038,最小正有限非零浮点是1.40 * 10-45
Double 最大正有限双是1.7976931348623157 * 10308,最小正有限非零双是4.9 * 10-324

6、 常量值

  • 整数常量
  • 浮点常量
  • 布尔常量
  • 字符串常量: 字符串常量是用双引号或三重双引号括起来的字符序列,即“”“...”“”。
  • 字符常量
  • 符号常量
  • 函数常量
  • 元组常量

7、Nothing 和 NUll类型

Null是所有引用类型的子类型。它是所有AnyRef类型的子类型,为关键字null提供类型。

Scala没有null关键字。

例如,不可能为scala.Int类型的变量分配null。

对于影响程序流程的操作,没有任何提供兼容的返回类型。

Nothing的用法之一是它发出异常终止的信号。

任何时候,如果你想使用null,请改用Option

8、Scala选项

Option 允许我们在没有null“hack”的情况下显式地表达空值。

Option是一个抽象类,它的两个具体子类是Some,当我们有一个值,而None,当我们没有。

def main(args: Array[String]) {val stateCapitals = Map( "Alabama" -> "Montgomery", "Alabama" -> "Montgomery1", "Alaska"  -> "Juneau", "Wyoming" -> "Cheyenne")println( "Get the capitals wrapped in Options:" )println( "Alabama: " + stateCapitals.get("Alabama") )println( "Wyoming: " + stateCapitals.get("Wyoming") )println( "Unknown: " + stateCapitals.get("Unknown") )println( "Get the capitals themselves out of the Options:" )println( "Alabama: " + stateCapitals("Alabama") )println( "Wyoming: " + stateCapitals.getOrElse("Wyoming", "Oops!") )println( "Unknown: " + stateCapitals.getOrElse("Alaska", "Oops2!") )println( "Unknown: " + stateCapitals.getOrElse("Unknown", "Oops2!") )}

注意

Map.get方法返回一个Option [T],在这种情况下T是String。

通过返回一个选项,我们不能“忘记”我们必须验证返回的东西。

如果OptionSome,则Some.get返回值。

如果Option实际上是None,那么None.get将抛出一个NoSuchElementException异常。

在最后两个println语句中的getOrElse返回Option中的值,如果它是一个Some实例,或者返回传递给getOrElse的参数,如果它是一个None实例。

getOrElse 参数作为默认返回值。

9、Scala范围

有些代码需要从一些开始到结束创建一个数字序列。一个Range常量量是我们需要的。

范围可以通过它们的开始,结束和步进值来定义。

要在Scala中创建范围,请使用预定义的方法,如以下代码所示:

object Main {def main(args: Array[String]) { println(1 to 5  )}}

我们还可以使用预定义的方法创建一个具有上限(不包括其上限)的范围,直到如下代码所示。

object Main {def main(args: Array[String]) { println(1 until 5 )}}

对于1到5,创建范围(1,2,3,4,5),但对于1到5,创建具有上限独占范围(1,2,3,4)的范围。

我们还可以使用预定义的方法创建一个带有步进值的范围,如下面的代码所示。

object Main {def main(args: Array[String]) { println(1 to 20 by 4  )}}

10、Scala元组

元组是具有相同或不同类型的两个或更多个值的有序容器。

然而,与列表和数组不同,没有办法迭代元组中的元素。

它的目的只是作为一个多个值的容器。

元组在需要组合离散元素并提供结构化数据的通用方法时非常有用。

我们可以通过两种方式创建一个元组:

  • 通过用逗号分隔的值写入值,并用一对括号括起来
  • 通过使用关系运算符->

例子

以下代码显示了一个包含Int,一个布尔值和一个String的元组,使用前一个方法。

val tuple = (1, false, "Scala")

以下代码显示了使用关系运算符创建的元组:

val tuple2 ="title" -> "Beginning Scala"

元组的单个元素可以通过其索引访问,其中第一个元素具有索引1。

以下代码显示了访问元组的第三个元素。

val tuple = (1, false, "Scala")val third = tuple._3

Scala 单元类型

单元类型用于定义不返回数据的函数。它类似于Java中的void关键字。

def main(args: Array[String]) : Unit = { } 

Scala语句

1、Scala条件运算符

Scala条件运算符在下表中列出。

运算符 操作 描述
&& 运算符左侧和右侧的值为true。 仅当左侧为真时,右侧才被计算。
|| 左侧或右侧的至少一个值为true。仅当左边为假时才计算右侧。
> 大于 左侧的值大于右侧的值。
>= 大于或等于 左侧的值大于或等于右侧的值。
< 少于 左侧的值小于右侧的值。
<= 小于或等于左侧的值小于或等于右侧的值。
== 等于 左侧的值与右侧的值相同。
!= 不等于 左侧的值与右侧的值不同。

注意

&& ||是“短路"运算符。 一旦知道答案,他们就停止计算表达式。

在Java中,==仅比较对象引用。它不会执行逻辑等同性检查,即比较字段值。使用equals方法。

Scala使用==作为逻辑等式,但它调用equals方法。

当您想要比较引用,但不测试逻辑时,可以使用新的方法 eq

2、IF

Scala中的if表达式的结果始终为Unit。

if/else的结果基于表达式的每个部分的类型。

如果exp是true,上面的代码打印“是”。if (exp) println("yes")像Java一样,if表达式可能有一个多行代码块。if (exp) { println("Line one") println("Line two")}Scala中的if/else在Java中的行为类似于三元运算符:val i: Int = if (exp) 1 else 3并且表达式的任一(或两者)部分可以具有如下面代码中所示的多行代码块。val i: Int = if (exp)             1          else {             val j = System.currentTimeMillis             (j % 100L).toInt          }

3、for循环

A For Comprehension是一个非常强大的Scala语言的控制结构。

它提供了迭代集合的能力,它还提供过滤选项和生成新集合的能力。

让我们从表达式的基本开始:

object Main {def main(args: Array[String]) {  val dogBreeds = List("A", "B", "C", "D", "E", "F")   for (breed <- dogBreeds)     println(breed) }}

生成器表达式
表达式 ‘breed <- dogBreeds’ 称为生成器表达式,因此命名是因为它从集合中生成单个值。

约束: 过滤值

我们可以添加if表达式来过滤我们想要保留的元素。

object Main {def main(args: Array[String]) { val books = List("Scala", "Groovy", "Java", "SQL", "CSS") for(book<-books     if book.contains("Scala") ) println(book)}}

可变绑定

我们可以为表达式定义变量。

然后我们可以在你的for表达式的正文中重用这些变量。

object Main {def main(args: Array[String]) { val books = List("Scala", "Groovy", "Java", "SQL", "CSS") for {     book <- books     bookVal = book.toUpperCase() } println(bookVal)}}

bookVal没有声明为val,但是你仍然可以重用它。

Yielding

在Scala的for表达式中,我们可以使用yield关键字来生成新的集合。

从for表达式生成的集合的类型从迭代的集合的类型推断。

要在for循环中将值赋给我们的程序的另一部分,请使用yield关键字为表达式生成新的集合。

object Main {def main(args: Array[String]) { val books = List("Scala", "Groovy", "Java", "SQL", "CSS") var scalabooks = for{     book <-books     if book.contains("Scala") }yield book println(scalabooks);}}过滤的结果作为名为book的值生成。这个结果是在for循环中每次运行时累积的,因此累积的集合被分配给值scalabooks。scalabook是List [String]类型,因为它是图书列表的一个子集,也是List [String]类型。

扩展范围和值定义

用于解释的Scala可以在for表达式的第一部分中定义可用于后面表达式的值,如下例所示:

object Main {def main(args: Array[String]) {  val dogBreeds = List("D", "Y", "D", "S", "G", "P")   for {     breed <- dogBreeds     upcasedBreed = breed.toUpperCase()   } println(upcasedBreed) }}

4、Scala while

  • while 循环
  • do-while循环
object Main {  def main(args: Array[String]) {    var thisWorld = 1    while (thisWorld < 10) {      println(" hi ... " + thisWorld)      thisWorld += 1    }    var count2 = 1    do {      count2 += 1      print(count2 + " ")    } while (count2 < 20)  }}

5、Scala try表达式

Scala中的异常处理以不同的方式实现,但它的行为与Java完全相同,并与现有的Java库无缝协作。

Scala中的所有异常都未选中;没有检查异常的概念。

1、抛出异常在Scala和Java中是一样的。throw new Exception("some exception...")2、try/finally结构在Scala和Java中也是一样的,如下面的代码所示: try { throw newException("some exception...")} finally{ println("This will always be printed")}3、try/catch在Scala是一个表达式,导致一个值。Scala中的异常可以在catch块中进行模式匹配,而不是为每个不同的异常提供单独的catch子句。因为Scala中的try/catch是一个表达式,所以可以在try / catch中包装调用,并在调用失败时分配默认值。以下代码显示了具有模式匹配catch块的基本try/catch表达式。try { file.write(stuff)} catch{ case e:java.io.IOException => // handle IO Exception case n:NullPointerException => // handle null pointer}例子: 以下代码显示了通过调用Integer.parseIntand在try/catch中包装调用的示例,如果调用失败,则分配默认值。try{Integer.parseInt("dog")}catch{case_ => 0}

6、Scala模式匹配

模式匹配允许我们在多个条件之间进行编程选择。 类似于JAVA的 case when

object Main extends App { def printNum(int: Int) {  int match {      case 0 => println("Zero")      case 1 => println("One")      case _ => println("more than one")  } } printNum(0) printNum(1) printNum(2)}

带下划线_的最后一种情况是通配符。它匹配任何未定义在上面的情况下。

Scala允许将守卫放置在模式中,以测试无法在模式声明本身中测试的特定条件。因此,如果传递负数,我们可以写入我们的Fibonacci 计算器返回0,如以下示例所示。def fib2(in: Int): Int = in match {case n if n <= 0 => 0case 1 => 1case n => fib2(n - 1) + fib2(n - 2)}

匹配任何类型

让我们考虑一个任何类型的元素的列表,包含一个String,一个Double,一个Int和一个Char。

object Main extends App { val anyList= List(1, "A", 2, 2.5, 'a') for (m <- anyList) {     m match {         case i: Int => println("Integer: " + i)         case s: String => println("String: " + s)         case f: Double => println("Double: " + f)         case other => println("other: " + other)     } }}

测试数据类型

下面的方法测试一个传入的Object,看看它是一个String,一个Integer,或者别的东西。def test2(in: Any) = in match { case s: String => "String, length "+s.length case i: Int if i > 0 => "Natural Int" case i: Int => "Another Int" case a: AnyRef => a.getClass.getName case _ => "null"}

7、Scala匹配表达式

Scala的匹配表达式用于模式匹配。

我们可以使用它在很少的代码中构造复杂的测试。

模式匹配就像Java的switch语句,但我们可以测试几乎任何东西,我们可以将匹配的值分配给变量。

Scala模式匹配是一个表达式,因此它产生可以分配或返回的值。

最基本的模式匹配就像Java的switch,除了在每种情况下没有中断,因为这些情况不会相互影响。

例子

以下代码将该数字与常量相匹配,但使用默认值。

44 match { case 44 => true// if we match 44,the result is true case _ => false// otherwise the result isfalse}

以下代码显示如何匹配字符串。s

"CSS" match { case "CSS"=> 45 // the result is 45 if we match "CSS" case "Elwood" => 77 case _ => 0}

Scala函数

1/Scala有函数和方法

Scala方法是一个具有名称和签名的类的一部分。 Scala中的函数是一个可以分配给变量的完整对象。

函数定义可以出现在源文件中的任何位置。

不带参数的函数

在Scala中定义函数,请使用def关键字,后跟方法名和方法体,如下所示。def hello() = {"Hello World!"} 等号=用作方法签名和方法体之间的分隔符。我们可以使用hello()或hello调用此函数。object Main {def main(args: Array[String]) {   def hello() = {"Hello World!"}    println(hello );}}注意: 我们还可以包括可选的返回类型,如下所示。def hello():String = {"Hello World!"} 我们可以从方法体中完全删除括号。def hello() = "Hello World!" 我们也可以从方法签名中删除括号。def hello = "Hello World!" 

带参数的函数

以下代码显示如何使用参数创建函数。def square (i:Int) = {i*i} 函数的主体是表达式, 其中最后一行变为函数的返回值。我们可以调用这个函数为square(2)。object Main {def main(args: Array[String]) {   def square (i:Int) = {i*i}    println(square(2) );}}我们可以在函数中提供多个参数。多个参数之间用逗号分隔,如以下示例所示。def add(x: Int, y: Int): Int = { x + y } 我们现在可以通过将实际参数传递给add函数来调用这个函数。object Main {def main(args: Array[String]) {   def add(x: Int, y: Int): Int = { x + y }    println(add(5, 5) );}}

2、函数式编程

Scala允许我们将函数表达为文字。

函数文字允许我们有一个函数类型的表达式,我们可以写一个短格式,而不声明它的名称。

函数类型可以是以下之一:

  • 可以为其分配函数的变量或参数的类型
  • 采用函数参数的高阶函数的参数
  • 返回函数的高阶函数的结果类型

函数常量以括号括起来的逗号分隔的参数列表开头,后跟箭头和函数体。

函数常量也称为匿名函数。

考虑一个add函数:val add = (x: Int, y: Int) => x + y使用函数常量,您可以定义添加函数,如下所示:(x: Int, y: Int) => x + y.函数常量被实例化为称为函数值的对象。函数对象扩展FunctionN traits中的一个,例如Function0,Function1等等直到Function22。根据函数中的参数数量,相应的FunctionN trait由编译器选择。对于具有两个参数的函数,编译器选择Function2作为底层类型。对于具有3个参数的函数,编译器选择Function3,对于具有4个参数的函数,Function4等。因为函数值是一个对象,它可以存储在一个变量中,它可以使用括号函数调用来调用,如下所示:object Main extends App {   val add = (a: Int, b: Int) => a + b   println(add(1, 2));}

一级函数和高阶函数

Scala函数是对象。在函数式编程中,函数是一等公民函数。 一等公民函数可以分配给变量,作为参数传递给另一个函数作为其他函数的值返回。将函数作为参数或返回函数的函数称为高阶函数。

函数作为变量

正如我们可以传递String,Int和其他变量一样,我们可以像一个变量一样传递一个函数。我们可以定义一个函数常量,然后将该常量赋值给一个变量。以下代码定义了一个函数常量,它接受一个I​​nt参数,并返回一个值,该值是传递的Int的两倍:(i: Int) => { i * 2 }我们现在可以将该函数常量分配给一个变量:val doubler = (i: Int) => { i * 2 }变量doubler是一个函数的实例,称为函数值。我们现在可以调用doubler,如下所示:doubler(2)doubler 是Function1 trait的一个实例,它定义了一个接受一个参数的函数。doubler 是使用关键字 val 创建并分配给变量的函数。

函数作为参数

1/我们可以创建一个函数或一个方法,它将一个函数作为参数。为此,首先定义一个将函数作为参数的方法。    def operation(functionparam:(Int, Int) => Int) {        println(functionparam(4,4))    }操作方法使用一个名为functionparam的参数,它是一个函数。functionparam函数接受两个Int并返回一个 Int 。操作方法返回一个Unit,指示操作方法不返回任何内容。2/接下来,定义一个与预期签名匹配的函数。以下add函数匹配该签名,因为它需要两个Int参数并返回Int:val add = (x: Int, y:Int) => { x + y }现在我们可以将一个add函数传递给操作方法:object Main extends App {    def operation(functionparam:(Int, Int) => Int) {        println(functionparam(4,4))    }    val add = (x: Int, y:Int) => { x + y }    operation(add)}3/任何匹配此签名的函数都可以传递到操作方法中。object Main extends App {    def operation(functionparam:(Int, Int) => Int) {        println(functionparam(4,4))    }    val add = (x: Int, y:Int) => { x + y }    operation(add)    val subtract = (x: Int, y:Int) => { x - y }    val multiply = (x: Int, y:Int) => { x*y }    operation(subtract)    operation(multiply)}

返回函数

我们可以从函数或方法返回一个函数。为了做到这一点,首先定义一个匿名函数。1/下面的代码声明一个匿名函数,它接受一个String参数并返回一个String:(name: String) => { "hello" + " " + name }2/现在我们将定义一个方法来返回我们刚才定义的匿名函数。def greeting() = (name: String) => {"hello" + " " + name}3/在=符号的左侧有一个正常的方法声明:def greeting()4/在右边的是一个函数文字:def greeting() = (name: String) => {"hello" + " " + name}5/现在你可以将greeting()赋给一个变量:val greet= greeting()6/因为匿名函数接受一个String参数名,我们可以传递一个名字:object Main extends App {    def greeting() = (name: String) => {"hello" + " " + name}    val greet= greeting()    println(greet("Scala"))}

Scala 类

1、类

类是创建对象的蓝图,对象是类的具体实例。

类定义包括字段声明和方法定义。

字段用于存储对象的状态,方法可以提供对字段的访问,并更改对象的状态。

让我们从创建Book对象的蓝图的一个简单示例开始:

class Book前面的Scala声明对应于此Java声明:public class Book {}定义类后,您可以使用关键字new创建类中的对象。要创建Book的实例,您可以键入以下内容:new Book这同样的效果,如下所示︰new Book()

举例:

以下代码创建一个类来表示形状。我们定义了一个名为`Shape`的超类型,它有一个计算形状面积的方法区域。class Shape {def area:Double = 0.0}

以下代码为RectangleCircle创建类。

class Rectangle(val width:Double,val height:Double) extends Shape {override def area:Double = width*height}class Circle(val radius:Double) extends Shape {override def area:Double = math.Pi*radius*radius}

每个类都接受一些参数并扩展Shape,然后覆盖Shape的方法。

子类型保证具有超类型的所有成员。

更改超类的方法的实现称为覆盖。

注意

我们不能改变Rectangle的宽度和高度以及Circle对象的半径,因为如果该字段是一个val ,Scala只会生成一个getter方法。

这是封装的示例。在封装中,对象的字段只能通过其方法访问。

我们可以编写接受Shape实例的代码,然后将其传递给RectangleCircle的实例:

def draw(s:Shape)

现在,考虑对这个函数的两个调用,像这样:

val circle = draw(new Circle(3))val rectangle = draw(new Rectangle(2,3))

继承保证任何我们可以在Shape实例上调用的方法都将在子类型中定义。

2、Scala值类

使用值类,Scala允许扩展AnyVal的用户定义的值类。

Scala 值类使我们能够在Scala类型层次结构的AnyVal一侧编写类。

Scala中的值类不分配运行时对象。

值类允许我们将扩展方法添加到类型,而不需要创建实例的运行时开销。

这是通过定义新的AnyVal子类来实现的。

举例:

下面说明了一个值类定义:

class SomeClass(val underlying: Int) extends AnyVal

前面的SomeClass类有一个单一的公共val参数,它是基础运行时表示。

编译时的类型是SomeClass,但在运行时,表示是一个Int

值类可以定义defs,但不能定义vals,vars或嵌套的traits类或对象。

以下代码说明了值类SomeClass中的def

值类只能扩展一个通用特征。

class SomeClass(val i: Int) extends AnyVal { def twice() = i*2}

这里SomeClass是一个用户定义的值类,它包装Int参数并封装两次方法。

要调用两次方法,请按如下所示创建SomeClass类的实例:

val v = new SomeClass(9)v.twice()

3、方法声明

Scala方法声明具有def关键字,方法名称,参数,可选返回类型,= 关键字,

方法body.myMethod不带参数并返回String:def myMethod():String = "www.w3cschool.cn"myOtherMethod 不带参数并返回一个String,但是返回类型没有被显式声明,因为编译器推断返回类型。def myOtherMethod() = "Moof"我们在方法声明的括号内声明参数。参数名称后面必须是参数的类型:def foo(a: Int):String = a.toString我们可以声明多个参数:def f2(a: Int, b:Boolean):String= if (b)a.toString else"false"

4、case类

Scala可以创建具有常见内容填充的类。

大多数时候,当我们定义一个类时,我们必须编写toString,hashCode和equals方法。

Scala提供了用于填充这些空白的case类机制,以及支持模式匹配。

case类提供与普通类相同的功能,但编译器生成toString,hashCode和equals方法,您可以覆盖。

可以在不使用新语句的情况下实例化Case类。

默认情况下,case类的构造函数中的所有参数都成为case类的属性。

以下是创建case类的方法:

case class Stuff(name:String, age: Int)

5、Scala对象

Scala没有静态成员。相反,Scala有单例对象。

单例对象定义看起来像一个类定义,除了使用关键字对象而不是关键字类。

单例是一个只能有一个实例的类。

例如,我们可以创建一个单例对象来表示一个Car,像这样:

object Car {def drive {   println("drive car")}}

将Car定义为一个对象,它只能有一个实例,我们可以像Java类上的静态方法一样调用它的方法:

Car.drive

与类不同,单例对象不能接受参数。

注意

我们可以将singleton对象用于许多目的,包括收集相关的实用程序方法,或者定义一个到Scala应用程序的入口点。

有两种方法为应用程序创建启动点:使用正确定义的main方法定义对象,或定义对象或扩展App特性。

对于第二种方法,定义一个扩展App trait的对象,如下所示:

object Main extends App { println("Hello, world")}

Scala提供了一个trait,scala.你的singleton对象应该扩展以启动应用程序的应用程序。

然后你将放在main方法中的代码直接放在singleton对象中。

6、Scala Traits

sTraits就像Java中的接口,它也可以包含代码。

在Scala中,当一个类从trait继承时,它实现trait的接口,并继承trait中包含的所有代码。

在Scala中,traits可以继承类。

当一个类继承一个trait作为其父类时,也使用关键字extends。

即使当类使用with关键字在其他traits中混合时,也使用关键字extends。

此外,当一个trait是另一个trait或类的子对象时使用extends。

7、继承

Scala支持单继承,而不是多重继承。

子类可以只有一个父类。

Scala类层次结构的根是Any,没有父类。

class Vehicle (speed : Int){ val mph :Int = speed def race() = println("Racing")}

Vehicle类采用一个参数,即车辆的速度。

创建类Vehicle的实例时,必须传递此参数,如下所示:

new Vehicle(100)

该类包含一个方法,称为race

8、构造函数

  • 参数声明为val

如果构造函数参数声明为val,Scala只为它生成一个getter方法。

让我们声明一个字段为val,如下所示:

class Book( val title:String)

因为构造函数字段被定义为一个val,所以该字段的值是不可变的。因此,Scala只生成getter方法,没有setter方法。

  • 参数声明为var

如果构造函数参数声明为var,Scala将生成访问器和mutator方法。

class Book( var title:String)

我们可以改变Book对象的字段,因为它是用关键字var声明的。

  • 参数声明为私有val或var

您可以将private关键字添加到valvar字段,以防止getter和setter方法生成。

在这种情况下,字段只能从类的成员内访问:

class Book(private var title: String) { def printTitle {    println(title) }}val book = new Book("Beginning Scala")println(book.printTitle )
  • 参数声明没有val或 var

当在构造函数参数上未指定val和var时,Scala不生成getter或setter。

举例:

这里是Book类,一个名为title的构造函数参数,默认值为“Scala”。因为参数使用默认值定义,您可以调用构造函数而不指定标题值:class Book (val title: String = "Scala")val book = new Bookbook.title您还可以在创建新图书时指定所选的标题值:val book = new Book("new title")book.title您还可以选择提供命名参数,如以下代码所示:val book = new Book(title="Beginning Scala")book.title
  • 辅助构造函数

我们可以为类定义一个或多个辅助构造函数,以提供创建对象的不同方法。

辅助构造函数通过创建名为this的方法来定义。

我们可以定义多个辅助构造函数,但它们必须有不同的签名。

每个辅助构造函数必须以对先前定义的构造函数的调用开始。

以下代码说明了一个主构造函数和两个辅助构造函数。

class Book (var title :String, var ISBN: Int) {    def this(title: String) {        this(title, 2222)    }    def this() {        this("CSS")        this.ISBN = 1111    }    override def toString = s"$title ISBN- $ISBN"}

给定这些构造函数,可以通过以下方式创建同一本书:

val book1 = new Bookval book2 = new Book("Clojure")val book3 = new Book("Scala", 3333)输出结果为: CSS ISBN- 1111Clojure ISBN- 2222Scala ISBN- 3333

辅助构造函数只需要调用先前定义的构造函数之一。

9、伴生对象

在Scala中,类和对象可以共享同一个名称。

当一个对象与一个类共享一个名称时,它被称为伴生对象,并且该类被称为伴生类。

伴生对象是与另一个类或特征共享相同名称和源文件的对象。

一个trait可以看作是一个Java接口。

这种方法允许我们在类上创建静态成员。

伴生对象对实现辅助方法和工厂很有用。

要实现一个创建不同类型的形状的工厂,我们可以在Scala中创建一个形状工厂。

我们使用一个伴生类Shape和一个伴生对象Shape,作为一个工厂。

举例:

trait Shape { def area :Double}object Shape { private class Circle(radius: Double) extends Shape{     override val area = 3.14*radius*radius } private class Rectangle (height: Double, length: Double)extends Shape{     override val area = height * length } def apply(height :Double , length :Double ) : Shape = new Rectangle(height,length) def apply(radius :Double) : Shape = new Circle(radius)}object Main extends App { val circle = Shape(2) println(circle.area) val rectangle = Shape(2,3) println(rectangle.area)}注意与伴生类不共享相同名称的单例对象称为独立对象。

10、闭包

闭包是一个函数,它的返回值取决于在该函数之外声明的一个或多个变量的值。

例子

让我们创建一个乘法器函数如图所示:

var y = 3val multiplier = (x:Int) => x * y

现在,y具有对函数外部的变量的引用,但是在包围范围中。

object Main extends App {  var y = 3 val multiplier = (x:Int) => x * y println(multiplier(3))} 

部分应用功能

在函数式编程语言中,调用带有参数的函数是将函数应用于参数。

当所有参数都传递给函数时,我们将函数完全应用于所有参数。

一个简单的添加函数:

val add = (x: Int, y: Int) => x + yadd(1,2)

但是当我们只给函数一个子集的参数,表达式的结果是一个部分应用的函数。

val partiallyAdd = add(1, _:Int)

因为我们没有为第二个参数提供值,所以变量部分Add是一个部分应用的函数。

当给予partiallyAdd一个Int值2时,你得到传递到add和partiallyAdd函数的Int数的总和:

partiallyAdd(2)

当我们提供所有参数时,执行原始函数,产生结果。

Curried函数

Currying转换具有多个参数的函数,创建一个函数链,每个函数都需要一个参数。

下面的代码创建了添加两个Int参数a和b的`add`函数,如下所示:val add = (x: Int, y: Int) => x + yadd(3,3)在Scala中,curried函数使用多个参数列表定义,如下所示:def add(x: Int)(y: Int) = x + y我们还可以使用以下语法定义curried函数:def add(x: Int) = (y: Int) => x + y而不是两个Int参数的一个列表,您应用curry add函数到一个Int参数的两个列表。因此curry add函数看起来像这样:def curriedAdd(a: Int)(b: Int) = a + bcurriedAdd(2)(2)

11、导入

要在包中使用声明,我们必须导入它们,就像在Java中一样。

Scala提供了其他选项,如下面导入Java类型的示例所示:

包是一个命名的代码模块。

Java和Scala惯例规定程序包名称是代码所有者的反向域名。

举例:

  import java.awt._   import java.io.File   import java.io.File._   import java.util.{Map, HashMap} 

我们可以在包中使用下划线_作为通配符导入所有类型。

我们还可以导入单个Scala或Java类型,如第二行所示。

Java使用“星号"字符*作为导入的通配符。

在Scala中,此字符被允许作为方法名称,因此使用_来避免歧义。

第三行导入java.io.File中的所有静态方法和字段。

等效的Java导入语句将是import static java.io.File。*;

最后,我们可以导入一个类或对象并重命名它。例如,您可以从scala.util.parsing.json包中导入JSON类/对象,并将其重命名为JsonParser:

import scala.util.parsing.json.{JSON=> JsonParser}

import可以在任何代码块内部使用,导入将仅在该代码块的范围内有效。

例如,我们可以在类体中导入一些东西,如下面的代码所示:

class Frog { import scala.xml._ //}

例如,我们可以在类体中导入一些东西,如下面的代码所示:

这很像Java的静态导入。

组合局部范围导入和导入对象允许我们微调导入对象及其关联方法的位置。

注意

Scala没有导入静态构造,因为它像其他类型一样对待对象类型。

我们可以将import语句几乎放在任何地方,以将它们的可见性限制在需要它们的地方。

我们可以在导入时重命名类型,我们可以禁止不需要的类型的可见性:

  def stuffWithBigInteger() = {     import java.math.BigInteger.{       ONE => _,       TEN,       ZERO => JAVAZERO }     println( "TEN: "+TEN )     println( "ZERO: "+JAVAZERO )   } 

导入是相对的

Scala导入是相对的。

请注意以下导入的注释:

  import scala.collection.mutable._   import collection.immutable._              // Since "scala" is already imported   import _root_.scala.collection.parallel._  // full path from real "root" 

Scala集合

Scala数组

数组是由相同类型的元素的集合组成的数据结构。

元素与索引相关联,索引通常为整数,用于访问或替换特定元素。

有两种方法来定义数组:指定元素的总数,然后将值分配给元素,或者我们可以一次指定所有值。

举例:

以下代码显示了如何创建一个可以包含三个元素的字符串数组。

var books:Array[String] = new Array[String](3) 

这里书籍被声明为一个可以容纳三个元素的字符串数组。我们可以简化声明如下。

var books = new Array[String](3) 

我们可以定义books数组并赋值如下。

var books = Array("Scala", "Java", "Groovy") 

我们可以使用如下所示的命令为各个元素赋值或访问各个元素:

object Main {def main(args: Array[String]) { var books = new Array[String](3)  books(0) = "Scala";  books(1) = "Java";  books(2) = "Groovy"  println(books(0)) }}

数组的第一个元素的索引是数字0,最后一个元素的索引是元素的总数减去1。

Scala列表

在Scala列别中,所有元素都具有类似数组的类型,但与数组不同,列表的元素不能通过复制进行更改。

具有类型T的元素的列表被写为List[T]

有两种方法来创建列表:

  • 以与创建数组类似的方法创建列表
  • use :: cons 运算符

例子

首先我们将展示更传统的方法。以下代码显示了如何创建空列表。

val empty: List[Nothing] = List() 

注意,列表的类型是Nothing。

我们可以创建如下列代码所示的书籍列表:

val books: List[String] = List("Scala", "Groovy", "Java") 

这两个列表可以使用tailNil::定义。

Nil也表示空列表。

可以使用Nil定义空列表。

val empty = Nil 

书籍列表可以使用尾部Nil和::定义,如下面的代码所示。

val books = "Scala" :: ("Groovy" :: ("Java" :: Nil)) 

列表上的操作可以用head和tail方法表示,其中head返回列表的第一个元素,tail返回一个由除第一个元素之外的所有元素组成的列表。

object Main {  def main(args: Array[String]) {    val books = "Scala" :: ("Groovy" :: ("Java" :: Nil))     println(books.head )    println(books.tail )  }}

总结

这篇笔记主要写了 一个HelloWorld 以及 Scala的基础语法。

撒花

posted @ 2021-06-25 09:01  我不是铁杆啊  阅读(275)  评论(0编辑  收藏  举报