来自 电脑知识 2019-10-02 07:57 的文章
当前位置: 威尼斯国际官方网站 > 电脑知识 > 正文

一篇入门,语法基础

一 简介

Scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。Scala 运行在Java虚拟机上,并兼容现有的Java程序。Scala 源代码被编译成Java字节码,所以它可以运行于JVM之上,并可以调用现有的Java类库。

整体介绍

威尼斯国际官方网站 1

  • Scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。
  • 联邦理工学院洛桑的Martin Odersky于2001年基于Funnel的工作开始设计Scala。Java平台的Scala于2003年底/2004年初发布。.NET平台的Scala发布于2004年6月。该语言第二个版本,v2.0,发布于2006年3月。
  • Scala 运行在Java虚拟机上,并兼容现有的Java程序。
  • Scala 源代码被编译成Java字节码,所以它可以运行于JVM之上,并可以调用现有的Java类库。

二 安装与环境配置

Scala 语言可以运行在Window、Linux、Unix、 Mac OS X等系统上。Scala是基于java之上,大量使用java的类库和变量,使用 Scala 之前必须先安装 Java(>1.5版本)。

Scala 特性

1 Java 安装设置(Linux 上安装 jdk

确保你本地以及安装了 JDK 1.5 以上版本,并且设置了 JAVA_HOME 环境变量及 JDK 的bin目录,scala2.11支持jdk1.8以上。

我们可以使用以下命令查看是否安装了 Java:java-version

java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

接着,我们可以查看是否安装了 Java 编译器。输入以下命令查看:

$ javac -version
javac 1.8.0_171

面向对象特性

Scala可以是一种面向对象的语言,每个值都可以是对象。

2 下载 Scala 二进制包

Scala 官网地址  下载 Scala 二进制包,这里我们将下载 2.11.12版本

函数式编程

Scala提供了轻量级的语法用以定义匿名函数,支持高阶函数,允许嵌套多层函数,并支持柯里化。

2.1 Liunx上安装scala

解压缩文件包,可将其移动至/opt/module/下:

[root@node21 tools]# tar zxvf scala-2.11.12.tgz  -C /opt/module/

配置环境变量,如果不是管理员可使用 sudo 进入管理员权限,修改配置文件profile:

[root@node21 module]# vi /etc/profile
#命令G 跳到文件末尾 命令o向下追加一行添加
export  SCALA_HOME=/opt/module/scala-2.11.12
export  PATH=$PATH:$SCALA_HOME/bin
#Esc键退出 :wq! 保存退出 
[root@node21 module]# source /etc/profile

执行 scala 命令,输出以下信息,表示安装成功:

[root@node21 module]# scala
Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.
scala> 
[root@node21 module]# scala -version
Scala code runner version 2.11.12 -- Copyright 2002-2017, LAMP/EPFL

注意:在编译的时候,如果有中文会出现乱码现象,解决方法查看:Scala 中文乱码解决

静态类型

Scala具备类型系统,通过编译时检查,保证代码的安全性和一致性。

2.2 window 上安装 Scala

扩展性

Scala提供了许多独特的语言机制,可以以库的形式轻易无缝添加新的语言结构:

  • 任何方法可用作前缀或后缀操作符
  • 可以根据预期类型自动构造闭包

威尼斯国际官方网站 2

下载后,双击 msi 文件,一步步安装即可,安装过程你可以使用默认的安装目录。安装好scala后,系统会自动提示,单击 finish,完成安装。

右击我的电脑,单击"属性",进入如图所示页面。下面开始配置环境变量,右击【我的电脑】--【属性】--【高级系统设置】--【环境变量】,如图:

威尼斯国际官方网站 3

设置 SCALA_HOME 变量:单击新建,在变量名栏输入:SCALA_HOME: 变量值一栏输入:D:Program Filesscala 也就是scala的安装目录

威尼斯国际官方网站 4

设置 Path 变量:找到系统变量下的"Path"如图,单击编辑。在"变量值"一栏的最前面添加如下的路径: %SCALA_HOME%bin;%SCALA_HOME%jrebin;注意:后面的分号  不要漏掉。

威尼斯国际官方网站 5

检查环境变量是否设置好了:调出"cmd"检查。单击 【开始】,在输入框中输入cmd,然后"回车",输入 scala,然后回车,如环境变量设置ok,你应该能看到这些信息。

威尼斯国际官方网站 6

并发性

Scala使用Actor作为其并发模型,Actor是类似线程的实体,通过邮箱发收消息。Actor可以复用线程,因此在程序中可以使用数百万个Actor,而线程只能创建数千个。在2.10之后的版本中,使用Akka作为其默认Actor实现。

三 基础语法

如果你之前是一名 Java 程序员,并了解 Java 语言的基础知识,那么你能很快学会 Scala 的基础语法。

Scala 与 Java 的最大区别是:Scala 语句末尾的分号 ; 是可选的。

我们可以认为 Scala 程序是对象的集合,通过调用彼此的方法来实现消息传递。接下来我们来理解下,类,对象,方法,实例变量的概念:

  • 对象 - 对象有属性和行为。例如:一只狗的状属性有:颜色,名字,行为有:叫、跑、吃等。对象是一个类的实例。

  • 类 - 类是对象的抽象,而对象是类的具体实例。

  • 方法 - 方法描述的基本的行为,一个类可以包含多个方法。

  • 字段 - 每个对象都有它唯一的实例变量集合,即字段。对象的属性通过给字段赋值来创建。

Scala 包

3.1第一个 Scala 程序

定义包

Scala 使用 package 关键字定义包,在Scala将代码定义到某个包中有两种方式:

3.1.1交互式编程

交互式编程不需要创建脚本文件,可以通过以下命令调用:

$ scala
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_31).
Type in expressions to have them evaluated.
Type :help for more information.

scala> 1 + 1
res0: Int = 2

scala> println("Hello World!")
Hello World!

scala>

第一种方法

和 Java 一样,在文件的头定义包名,这种方法就后续所有代码都放在该包中。 比如:

package com.runoobclass HelloWorld{    }

3.1.2脚本形式

我们也可以通过创建一个 HelloWorld.scala 的文件来执行代码,HelloWorld.scala 代码如下所示:

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

接下来我们使用 scalac 命令编译它:

$ scalac HelloWorld.scala 
$ ls
HelloWorld$.class    HelloWorld.scala
HelloWorld.class    

编译后我们可以看到目录下生成了 HelloWorld.class 文件,该文件可以在Java Virtual Machine (JVM)上运行。

编译后,我们可以使用以下命令来执行程序:

$ scala HelloWorld
Hello, world!

第二种方法

有些类似 C#,如:

package com.runoob {  class HelloWorld{    }}

第二种方法,可以在一个文件中定义多个包。

3.2基本语法

Scala 基本语法需要注意以下几点:

  • 区分大小写 -  Scala是大小写敏感的,这意味着标识Hello 和 hello在Scala中会有不同的含义。

  • 类名 - 对于所有的类名的第一个字母要大写。
    如果需要使用几个单词来构成一个类的名称,每个单词的第一个字母要大写。

    示例:class MyFirstScalaClass

  • 方法名称 - 所有的方法名称的第一个字母用小写。
    如果若干单词被用于构成方法的名称,则每个单词的第一个字母应大写。

    示例:def myMethodName()

  • 程序文件名 - 程序文件的名称应该与对象名称完全匹配(新版本不需要了,但建议保留这种习惯)。
    保存文件时,应该保存它使用的对象名称(记住Scala是区分大小写),并追加".scala"为文件扩展名。 (如果文件名和对象名称不匹配,程序将无法编译)。

    示例: 假设"HelloWorld"是对象的名称。那么该文件应保存为'HelloWorld.scala"

  • def main(args: Array[String]) - Scala程序从main()方法开始处理,这是每一个Scala程序的强制程序入口部分。

引用包

Scala 使用 import 关键字引用包。

import java.awt.Color  // 引入Color import java.awt._  // 引入包内所有成员 def handler(evt: event.ActionEvent) { // java.awt.event.ActionEvent  ...  // 因为引入了java.awt,所以可以省去前面的部分}

import语句可以出现在任何地方,而不是只能在文件顶部。import的效果从开始延伸到语句块的结束。这可以大幅减少名称冲突的可能性。

如果想要引入包中的几个成员,可以使用selector语法糖:

import java.awt.{Color, Font} // 重命名成员import java.util.{HashMap => JavaHashMap} // 隐藏成员import java.util.{HashMap => _, _} // 引入了util包的所有成员,但是HashMap被隐藏了

注意:默认情况下,Scala 总会引入 java.lang._ 、 scala._ 和 Predef._,这里也就解释了,为什么以scala开头的包,在使用时都是省去scala.的。

3.2.1标识符

Scala 可以使用两种形式的标志符,字符数字和符号。

字符数字使用字母或是下划线开头,后面可以接字母或是数字,符号"$"在 Scala 中也看作为字母。然而以"$"开头的标识符为保留的 Scala 编译器产生的标志符使用,应用程序应该避免使用"$"开始的标识符,以免造成冲突。

Scala 的命名规则采用和 Java 类似的 camel 命名规则,首字符小写,比如 toString。类名的首字符还是使用大写。此外也应该避免使用以下划线结尾的标志符以避免冲突。符号标志符包含一个或多个符号,如+,:,? 等,比如:

+ ++ ::: < ?> :->

Scala 内部实现时会使用转义的标志符,比如:-> 使用 $colon$minus$greater 来表示这个符号。因此如果你需要在 Java 代码中访问:->方法,你需要使用 Scala 的内部名称 $colon$minus$greater。

混合标志符由字符数字标志符后面跟着一个或多个符号组成,比如 unary_+ 为 Scala 对+方法的内部实现时的名称。字面量标志符为使用"定义的字符串,比如 `x` `yield`。

你可以在"之间使用任何有效的 Scala 标志符,Scala 将它们解释为一个 Scala 标志符,一个典型的使用为 Thread 的 yield 方法, 在 Scala 中你不能使用 Thread.yield()是因为 yield 为 Scala 中的关键字, 你必须使用 Thread.`yield`()来使用这个方法。

数据类型

Scala的数据类型和Java差不多,以下是和Java不同之处.

数据类型 描述
java... 和java一样,8种基本类型...
Unit 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
Null null 或空引用
Nothing Nothing类型在Scala的类层级的最低端;它是任何其他类型的子类型。
Any Any是所有其他类的超类
AnyRef AnyRef类是Scala里所有引用类(reference class)的基类

上表中列出的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是可以对数字等基础类型调用方法的。

3.2.2Scala 关键字

下表列出了 scala 保留关键字,我们不能使用以下关键字作为变量:

abstract case catch class
def do else extends
false final finally for
forSome if implicit import
lazy match new null
object override package private
protected return sealed super
this throw trait try
true type val var
while with yield  
- : = =>
<- <: <% >:
# @    

类型的层级关系

关于Scala的类型关系,可以看我的另一篇文章Scala的类层级讲解

3.2.3Scala 注释

Scala 类似 Java 支持单行和多行注释。多行注释可以嵌套,但必须正确嵌套,一个注释开始符号对应一个结束符号。注释在 Scala 编译中会被忽略,实例如下:

object HelloWorld {
   /* 这是一个 Scala 程序
    * 这是一行注释
    * 这里演示了多行注释
    */
   def main(args: Array[String]) {
      // 输出 Hello World
      // 这是一个单行注释
      println("Hello, world!") 
   }
}

符号字面量

符号字面量 'x 是表达式 scala.Symbol("x") 的简写;

3.2.4空行和空格

一行中只有空格或者带有注释,Scala 会认为其是空行,会忽略它。标记可以被空格或者注释来分割。

多行字符串的表示方法

val foo = """菜鸟教程www.runoob.comwww.w3cschool.ccwww.runnoob.com以上三个地址都能访问"""

3.2.5换行符

Scala是面向行的语言,语句可以用分号(;)结束或换行符。Scala 程序里,语句末尾的分号通常是可选的。如果你愿意可以输入一个,但若一行里仅 有一个语句也可不写。另一方面,如果一行里写多个语句那么分号是需要的。例如

val s = "菜鸟教程"; println(s)

Scala 变量

变量是一种使用方便的占位符,用于引用计算机内存地址,变量创建后会占用一定的内存空间。

3.2.6Scala 包

定义包

Scala 使用 package 关键字定义包,在Scala将代码定义到某个包中有两种方式:

第一种方法和 Java 一样,在文件的头定义包名,这种方法就后续所有代码都放在该包中。 比如:

package com.runoob
class HelloWorld

第二种方法有些类似 C#,如:

package com.runoob {
  class HelloWorld 
}

第二种方法,可以在一个文件中定义多个包。

引用

Scala 使用 import 关键字引用包。

import java.awt.Color  // 引入Color

import java.awt._  // 引入包内所有成员

def handler(evt: event.ActionEvent) { // java.awt.event.ActionEvent
  ...  // 因为引入了java.awt,所以可以省去前面的部分
}

import语句可以出现在任何地方,而不是只能在文件顶部。import的效果从开始延伸到语句块的结束。这可以大幅减少名称冲突的可能性。

如果想要引入包中的几个成员,可以使用selector(选取器):

import java.awt.{Color, Font}

// 重命名成员
import java.util.{HashMap => JavaHashMap}

// 隐藏成员
import java.util.{HashMap => _, _} // 引入了util包的所有成员,但是HashMap被隐藏了

变量声明

在 Scala 中,使用关键词 "var" 声明变量,使用关键词 "val" 声明常量。

var myVar : String = "Foo"val myVal : String = "Foo"

在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。
++所以,如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。++

四 数据类型

Scala 与 Java有着相同的数据类型,下表列出了 Scala 支持的数据类型:

数据类型 描述
Byte 8位有符号补码整数。数值区间为 -128 到 127
Short 16位有符号补码整数。数值区间为 -32768 到 32767
Int 32位有符号补码整数。数值区间为 -2147483648 到 2147483647
Long 64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807
Float 32位IEEE754单精度浮点数
Double 64位IEEE754单精度浮点数
Char 16位无符号Unicode字符, 区间值为 U+0000 到 U+FFFF
String 字符序列
Boolean true或false
Unit 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
Null null 或空引用
Nothing Nothing类型在Scala的类层级的最低端;它是任何其他类型的子类型。
Any Any是所有其他类的超类
AnyRef AnyRef类是Scala里所有引用类(reference class)的基类

上表中列出的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是可以对数字等基础类型调用方法的。

Scala 支持多个变量同时的声明赋值:

val xmax, ymax = 100  // xmax, ymax都声明为100

4.1Scala 基础字面量

Scala 非常简单且直观。接下来我们会详细介绍 Scala 字面量。

访问修饰符

分别有:private,protected,public。

4.1.1整型字面量

整型字面量用于 Int 类型,如果表示 Long,可以在数字后面添加 L 或者小写 l 作为后缀。:

0
035
21 
0xFFFFFFFF 
0777L

私有

Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。

class Outer{    class Inner{        private def f(){            println("f")        }        class InnerMost{            f() // 正确        }    }    (new Inner).f() //错误}

4.1.2浮点型字面量

如果浮点数后面有f或者F后缀时,表示这是一个Float类型,否则就是一个Double类型的。实例如下:

0.0 
1e30f 
3.14159f 
1.0e100
.1

保护(Protected)

对保护(Protected)成员的访问比 java 更严格一些。
因为它只允许保护成员在定义了该成员的的类的子类中被访问,同包不可访问。

package p{    class Super{        protected def f() {            println("f")        }    }    class Sub extends Super{        f()    }    class Other{        (new Super).f() //错误    }}

4.1.3布尔型字面量

布尔型字面量有 true 和 false。

公共

如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问。

4.1.4符号字面量

符号字面量被写成: '<标识符> ,这里 <标识符> 可以是任何字母或数字的标识(注意:不能以数字开头)。这种字面量被映射成预定义类scala.Symbol的实例。

如: 符号字面量 'x 是表达式 scala.Symbol("x") 的简写,符号字面量定义如下:

 

package scala
final case class Symbol private (name: String) {
   override def toString: String = "'" + name
}

作用域保护

Scala中,访问修饰符可以通过使用限定词强调。格式为:

private[x] 或 protected[x]

这里的x指代某个所属的包、类或单例对象。如果写成private[x],读作"这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对象可见外,对其它所有类都是private。

这种技巧在横跨了若干包的大型项目中非常有用,它允许你定义一些在你项目的若干子包中可见但对于项目外部的客户却始终不可见的东西。

package bobsrocckets{    package navigation{        private[bobsrockets] class Navigator{            protected[navigation] def useStarChart(){}            class LegOfJourney{                private[Navigator] val distance = 100            }            private[this] var speed = 200        }    }    package launch{        import navigation._        object Vehicle{            private[launch] val guide = new Navigator        }    }}

上述例子中,类Navigator被标记为private[bobsrockets]就是说这个类对包含在bobsrockets包里的所有的类和对象可见。
比如说,从Vehicle对象里对Navigator的访问是被允许的,因为对象Vehicle包含在包launch中,而launch包在bobsrockets中,相反,所有在包bobsrockets之外的代码都不能访问类Navigator。

4.1.5字符字面量

在scala中字符类型表示为半角单引号(')中的字符,如下:

'a' 
'u0041'
'n'
't'

其中  表示转移字符,其后可以跟 u0041 数字或者 rn 等固定的转义字符。

Scala 运算符

4.1.6字符串字面量

字符串表示方法是在双引号中(") 包含一系列字符,如:

"Hello,nWorld!"
"菜鸟教程官网:www.runoob.com"

算术运算符

和java一样+ , - , * , / , %

4.2多行字符串的表示方法

多行字符串用三个双引号来表示分隔符,格式为:""" ... """

实例如下:

val foo = """菜鸟教程
www.runoob.com
www.w3cschool.cc
www.runnoob.com
以上三个地址都能访问"""

关系运算符

和java一样 == , != , > , < , >= , <=

4.3Null 值

空值是 scala.Null 类型。

Scala.Null和scala.Nothing是用统一的方式处理Scala面向对象类型系统的某些"边界情况"的特殊类型。

Null类是null引用对象的类型,它是每个引用类(继承自AnyRef的类)的子类。Null不兼容值类型。

逻辑运算符

和java一样&& , || , !

4.4Scala 转义字符

下表列出了常见的转义字符:

转义字符 Unicode 描述
b u0008 退格(BS) ,将当前位置移到前一列
t u0009 水平制表(HT) (跳到下一个TAB位置)
n u000a 换行(LF) ,将当前位置移到下一行开头
f u000c 换页(FF),将当前位置移到下页开头
r u000d 回车(CR) ,将当前位置移到本行开头
" u0022 代表一个双引号(")字符
' u0027 代表一个单引号(')字符
\ u005c 代表一个反斜线字符 ''

0 到 255 间的 Unicode 字符可以用一个八进制转义序列来表示,即反斜线‟‟后跟 最多三个八进制。

在字符或字符串中,反斜线和后面的字符序列不能构成一个合法的转义序列将会导致 编译错误。

以下实例演示了一些转义字符的使用:

object Test {
   def main(args: Array[String]) {
      println("HellotWorldnn" );
   }
}

执行以上代码输出结果如下所示:

$ scalac Test.scala
$ scala Test
Hello    World

位运算符

和java一样& , | , ^ , ~ , << , >> , >>>
~ 按位取反

无符号右移

五 变量

变量是一种使用方便的占位符,用于引用计算机内存地址,变量创建后会占用一定的内存空间。

基于变量的数据类型,操作系统会进行内存分配并且决定什么将被储存在保留内存中。因此,通过给变量分配不同的数据类型,你可以在这些变量中存储整数,小数或者字母。

赋值运算符

和java一样

5.1变量声明

在学习如何声明变量与常量之前,我们先来了解一些变量与常量。

  • 一、变量: 在程序运行过程中其值可能发生改变的量叫做变量。如:时间,年龄。
  • 二、常量 在程序运行过程中其值不会发生变化的量叫做常量。如:数值 3,字符'A'。

在 Scala 中,使用关键词 "var" 声明变量,使用关键词 "val" 声明常量。

声明变量实例如下:

var myVar : String = "Foo"
var myVar : String = "Too"

以上定义了变量 myVar,我们可以修改它。

声明常量实例如下:

val myVal : String = "Foo"

以上定义了常量 myVal,它是不能修改的。如果程序尝试修改常量 myVal 的值,程序将会在编译时报错。

流程语句

5.2变量类型声明

变量的类型在变量名之后等号之前声明。定义变量的类型的语法格式如下:

var VariableName : DataType [=  Initial Value]

或

val VariableName : DataType [=  Initial Value]

变量声明一定需要初始值,否则会报错。

if判断语句

和java一样,支持嵌套同时有返回值.

5.3变量类型引用

在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。

所以,如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。

var myVar = 10;
val myVal = "Hello, Scala!";

以上实例中,myVar 会被推断为 Int 类型,myVal 会被推断为 String 类型。

循环语句

和java完全一样,支持嵌套.

5.4Scala 多个变量声明

Scala 支持多个变量的声明:

val xmax, ymax = 100  // xmax, ymax都声明为100

如果方法返回值是元组,我们可以使用 val 来声明一个元组:

scala> val pa = (40,"Foo")
pa: (Int, String) = (40,Foo)

Scala 函数

Scala 有函数和方法,二者在语义上的区别很小。

Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。

我们可以在任何地方定义函数,甚至可以在函数内定义函数。更重要的一点是 Scala 函数名可以有以下特殊字符:+, ++, ~, &,-, -- , , /, : 等。

六 访问修饰符

Scala 访问修饰符基本和Java的一样,分别有:private,protected,public。

如果没有指定访问修饰符符,默认情况下,Scala 对象的访问级别都是 public。

Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。

函数声明

def functionName  : [return type]

如果你不写等于号和方法主体,那么方法会被隐式声明为"抽象",包含它的类也变成一个抽象类。

6.1私有成员

用 private 关键字修饰,带有此标记的成员仅在包含了成员定义的类或对象内部可见,同样的规则还适用内部类。

class Outer{
    class Inner{
    private def f(){println("f")}
    class InnerMost{
        f() // 正确
        }
    }
    (new Inner).f() //错误
}

(new Inner).f( ) 访问不合法是因为 f 在 Inner 中被声明为 private,而访问不在类 Inner 之内。

但在 InnerMost 里访问 f 就没有问题的,因为这个访问包含在 Inner 类之内。

Java中允许这两种访问,因为它允许外部类访问内部类的私有成员。

函数定义

def functionName  : [return type] = {   function body   return [expr]}

如下,一个求加法的函数:

object add{   def addInt( a:Int, b:Int ) : Int = {      var sum:Int = 0      sum = a + b      return sum   }}

如果函数没有返回值,可以返回为 Unit,这个类似于 Java 的 void.

Scala的灵活性,使的函数定义的方法多种多样:
1、规规矩矩的写法,带有等号、大括号和返回值类型的形式

def myFunc : Int = {    //something}def myFunc : Unit = {    //something}

2、非unit返回值的情形下,省略返回值,让程序根据代码块,自行判断。注意,这里等号还是要的

def myFunc = {    //something}

3、unit返回值的情况下,直接省略返回值类型和等号

def myFunc(var p1 : Int) {    //something    // return unit}

4、函数只有一行的情形下省略返回值和大括号

def max2(x: Int, y: Int) = if  x else y def greet() = println("Hello, world!")

6.2保护成员

在 scala 中,对保护(Protected)成员的访问比 java 更严格一些。因为它只允许保护成员在定义了该成员的的类的子类中被访问。而在java中,用protected关键字修饰的成员,除了定义了该成员的类的子类可以访问,同一个包里的其他类也可以进行访问。

package p{
class Super{
    protected def f() {println("f")}
    }
    class Sub extends Super{
        f()
    }
    class Other{
        (new Super).f() //错误
    }
}

上例中,Sub 类对 f 的访问没有问题,因为 f 在 Super 中被声明为 protected,而 Sub 是 Super 的子类。相反,Other 对 f 的访问不被允许,因为 other 没有继承自 Super。而后者在 java 里同样被认可,因为 Other 与 Sub 在同一包里。

函数调用

Scala 提供了多种不同的函数调用方式:
以下是调用方法的标准格式:

functionName

如果函数使用了实例的对象来调用,我们可以使用类似java的格式 :

[instance.]functionName

6.3公共成员

Scala中,如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问。

class Outer {
   class Inner {
      def f() { println("f") }
      class InnerMost {
         f() // 正确
      }
   }
   (new Inner).f() // 正确因为 f() 是 public
}

函数传名调用(call-by-name)

Scala的解释器在解析函数参数(function arguments)时有两种方式:

  • 传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
  • 传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部;

在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用是在函数内部进行参数表达式的值计算的。

这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。

object Test {   def main(args: Array[String]) {        delayed;   }   def time() = {      println("获取时间,单位为纳秒")      System.nanoTime   }   def delayed( t: => Long ) = {      println("在 delayed 方法内")      println("参数: " + t)      t   }}

以上实例中我们声明了 delayed 方法, 该方法在变量名和变量类型使用 => 符号来设置传名调用。执行以上代码,输出结果如下:

在 delayed 方法内获取时间,单位为纳秒参数: 241550840475831获取时间,单位为纳秒

分析可得,delayed参数先是没有调用,在打印t时再计算调用的.

我们把t: => Long改成t: Long,打印结果就变为:

获取时间,单位为纳秒在 delayed 方法内参数: 241550840475831

可能现在大家有些乱,如果把这里的t: => Long改成t: () => Long我想大家就清楚了,方便记忆,可以认为这里的t并不是一个值类型参数,而是一个函数类型的参数.

6.4作用域保护

Scala中,访问修饰符可以通过使用限定词强调。格式为:

private[x] 

或 

protected[x]

这里的x指代某个所属的包、类或单例对象。如果写成private[x],读作"这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对像可见外,对其它所有类都是private。

这种技巧在横跨了若干包的大型项目中非常有用,它允许你定义一些在你项目的若干子包中可见但对于项目外部的客户却始终不可见的东西。

package bobsrocckets{
    package navigation{
        private[bobsrockets] class Navigator{
         protected[navigation] def useStarChart(){}
         class LegOfJourney{
             private[Navigator] val distance = 100
             }
            private[this] var speed = 200
            }
        }
        package launch{
        import navigation._
        object Vehicle{
        private[launch] val guide = new Navigator
        }
    }
}

上述例子中,类Navigator被标记为private[bobsrockets]就是说这个类对包含在bobsrockets包里的所有的类和对象可见。

比如说,从Vehicle对象里对Navigator的访问是被允许的,因为对象Vehicle包含在包launch中,而launch包在bobsrockets中,相反,所有在包bobsrockets之外的代码都不能访问类Navigator。

指定函数参数名

只是调用的时候,使用=指定参数而已,比如:

object Test {   def main(args: Array[String]) {        printInt;   }   def printInt( a:Int, b:Int ) = {      println("Value of a : " + a );      println("Value of b : " + b );   }}

七 运算符

一个运算符是一个符号,用于告诉编译器来执行指定的数学运算和逻辑运算。

Scala 含有丰富的内置运算符,包括以下几种类型:

  • 算术运算符

  • 关系运算符

  • 逻辑运算符

  • 位运算符

  • 赋值运算符

接下来我们将为大家详细介绍以上各种运算符的应用。

可变参数

Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表。
Scala 通过在参数的类型之后放一个星号来设置可变参数。例如:

object Test {   def main(args: Array[String]) {        printStrings("Runoob", "Scala", "Python");   }   def printStrings( args:String* ) = {      var i : Int = 0;      for( arg <- args ){         println("Arg value[" + i + "] = " + arg );         i = i + 1;      }   }}

7.1算术运算符

下表列出了 Scala 支持的算术运算符。

假定变量 A 为 10,B 为 20:

运算符 描述 实例
+ 加号 A + B 运算结果为 30
- 减号 A - B 运算结果为 -10
* 乘号 A * B 运算结果为 200
/ 除号 B / A 运算结果为 2
% 取余 B % A 运算结果为 0

实例

object Test {
   def main(args: Array[String]) {
      var a = 10;
      var b = 20;
      var c = 25;
      var d = 25;
      println("a + b = " + (a + b) );
      println("a - b = " + (a - b) );
      println("a * b = " + (a * b) );
      println("b / a = " + (b / a) );
      println("b % a = " + (b % a) );
      println("c % a = " + (c % a) );

   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
a + b = 30
a - b = -10
a * b = 200
b / a = 2
b % a = 0
c % a = 5

递归函数

递归函数意味着函数可以调用它本身。常用的比如计算阶乘.

7.2关系运算符

下表列出了 Scala 支持的关系运算符。

假定变量 A 为 10,B 为 20:

运算符 描述 实例
== 等于 (A == B) 运算结果为 false
!= 不等于 (A != B) 运算结果为 true
> 大于 (A > B) 运算结果为 false
< 小于 (A < B) 运算结果为 true
>= 大于等于 (A >= B) 运算结果为 false
<= 小于等于 (A <= B) 运算结果为 true

实例

object Test {
   def main(args: Array[String]) {
      var a = 10;
      var b = 20;
      println("a == b = " + (a == b) );
      println("a != b = " + (a != b) );
      println("a > b = " + (a > b) );
      println("a < b = " + (a < b) );
      println("b >= a = " + (b >= a) );
      println("b <= a = " + (b <= a) );
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
a == b = false
a != b = true
a > b = false
a < b = true
b >= a = true
b <= a = false

默认参数值

在定义函数的时候使用=设置默认值,如:

def addInt( a:Int=5, b:Int=7 ) : Int = { }

7.3逻辑运算符

下表列出了 Scala 支持的逻辑运算符。

假定变量 A 为 1,B 为 0:

运算符 描述 实例
&& 逻辑与 (A && B) 运算结果为 false
|| 逻辑或 (A || B) 运算结果为 true
! 逻辑非 !(A && B) 运算结果为 true

实例

object Test {
   def main(args: Array[String]) {
      var a = true;
      var b = false;

      println("a && b = " + (a&&b) );

      println("a || b = " + (a||b) );

      println("!(a && b) = " + !(a && b) );
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
a && b = false
a || b = true
!(a && b) = true

高阶函数(Higher-Order Function)

Scala 中允许使用高阶函数,高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。

7.4位运算符

威尼斯国际官方网站 ,位运算符用来对二进制位进行操作,~,&,|,^分别为取反,按位与与,按位与或,按位与异或运算,如下表实例:

p q p & q p | q p ^ q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

如果指定 A = 60; 及 B = 13; 两个变量对应的二进制为:

A = 0011 1100

B = 0000 1101

-------位运算----------

A&B = 0000 1100

A|B = 0011 1101

A^B = 0011 0001

~A  = 1100 0011

Scala 中的按位运算法则如下:

运算符 描述 实例
& 按位与运算符 (a & b) 输出结果 12 ,二进制解释: 0000 1100
| 按位或运算符 (a | b) 输出结果 61 ,二进制解释: 0011 1101
^ 按位异或运算符 (a ^ b) 输出结果 49 ,二进制解释: 0011 0001
~ 按位取反运算符 (~a ) 输出结果 -61 ,二进制解释: 1100 0011, 在一个有符号二进制数的补码形式。
<< 左移动运算符 a << 2 输出结果 240 ,二进制解释: 1111 0000
>> 右移动运算符 a >> 2 输出结果 15 ,二进制解释: 0000 1111
>>> 无符号右移 A >>>2 输出结果 15, 二进制解释: 0000 1111

实例

object Test {
   def main(args: Array[String]) {
      var a = 60;           /* 60 = 0011 1100 */  
      var b = 13;           /* 13 = 0000 1101 */
      var c = 0;

      c = a & b;            /* 12 = 0000 1100 */ 
      println("a & b = " + c );

      c = a | b;            /* 61 = 0011 1101 */
      println("a | b = " + c );

      c = a ^ b;            /* 49 = 0011 0001 */
      println("a ^ b = " + c );

      c = ~a;               /* -61 = 1100 0011 */
      println("~a = " + c );

      c = a << 2;           /* 240 = 1111 0000 */
      println("a << 2 = " + c );

      c = a >> 2;           /* 15 = 1111 */
      println("a >> 2  = " + c );

      c = a >>> 2;          /* 15 = 0000 1111 */
      println("a >>> 2 = " + c );
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
a & b = 12
a | b = 61
a ^ b = 49
~a = -61
a << 2 = 240
a >> 2  = 15
a >>> 2 = 15

内嵌函数

我么可以在 Scala 函数内定义函数,定义在函数内的函数称之为局部函数。

7.5赋值运算符

以下列出了 Scala 语言支持的赋值运算符:

运算符 描述 实例
= 简单的赋值运算,指定右边操作数赋值给左边的操作数。 C = A + B 将 A + B 的运算结果赋值给 C
+= 相加后再赋值,将左右两边的操作数相加后再赋值给左边的操作数。 C += A 相当于 C = C + A
-= 相减后再赋值,将左右两边的操作数相减后再赋值给左边的操作数。 C -= A 相当于 C = C - A
*= 相乘后再赋值,将左右两边的操作数相乘后再赋值给左边的操作数。 C *= A 相当于 C = C * A
/= 相除后再赋值,将左右两边的操作数相除后再赋值给左边的操作数。 C /= A 相当于 C = C / A
%= 求余后再赋值,将左右两边的操作数求余后再赋值给左边的操作数。 C %= A is equivalent to C = C % A
<<= 按位左移后再赋值 C <<= 2 相当于 C = C << 2
>>= 按位右移后再赋值 C >>= 2 相当于 C = C >> 2
&= 按位与运算后赋值 C &= 2 相当于 C = C & 2
^= 按位异或运算符后再赋值 C ^= 2 相当于 C = C ^ 2
|= 按位或运算后再赋值 C |= 2 相当于 C = C | 2

实例

object Test {
   def main(args: Array[String]) {
      var a = 10;    
      var b = 20;
      var c = 0;

      c = a + b;
      println("c = a + b  = " + c );

      c += a ;
      println("c += a  = " + c );

      c -= a ;
      println("c -= a = " + c );

      c *= a ;
      println("c *= a = " + c );

      a = 10;
      c = 15;
      c /= a ;
      println("c /= a  = " + c );

      a = 10;
      c = 15;
      c %= a ;
      println("c %= a  = " + c );

      c <<= 2 ;
      println("c <<= 2  = " + c );

      c >>= 2 ;
      println("c >>= 2  = " + c );

      c >>= 2 ;
      println("c >>= a  = " + c );

      c &= a ;
      println("c &= 2  = " + c );

      c ^= a ;
      println("c ^= a  = " + c );

      c |= a ;
      println("c |= a  = " + c );
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
c = a + b  = 30
c += a  = 40
c -= a = 30
c *= a = 300
c /= a  = 1
c %= a  = 5
c <<= 2  = 20
c >>= 2  = 5
c >>= a  = 1
c &= 2  = 0
c ^= a  = 10
c |= a  = 10

运算符优先级取决于所属的运算符组,它会影响算式的的计算。

实例: x = 7 + 3 * 2; 这里, x 计算结果为 13, 而不是 20,因为乘法(*) 高于加法(+), 所以它先计算 3*2 再加上 7。

查看以下表格,优先级从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最低的优先级。

类别 运算符 关联性
1 () [] 左到右
2 ! ~ 右到左
3 * / % 左到右
4 + - 左到右
5 >> >>> << 左到右
6 > >= < <= 左到右
7 == != 左到右
8 & 左到右
9 ^ 左到右
10 | 左到右
11 && 左到右
12 || 左到右
13 = += -= *= /= %= >>= <<= &= ^= |= 右到左
14 , 左到右

匿名函数

Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。
使用匿名函数后,我们的代码变得更简洁了。

var mul = (x: Int, y: Int) => x*y

八 IF...ELSE 语句

Scala IF...ELSE 语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。

可以通过下图来简单了解条件语句的执行过程:

威尼斯国际官方网站 7

偏应用函数

Scala 偏应用函数是一种表达式,你不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数。

如下实例,我们打印日志信息:

import java.util.Dateobject Test {   def main(args: Array[String]) {      val date = new Date      log(date, "message1" )      Thread.sleep      log(date, "message2" )      Thread.sleep      log(date, "message3" )   }   def log(date: Date, message: String)  = {     println(date + "----" + message)   }}

输出结果为:

Mon Dec 02 12:52:41 CST 2013----message1Mon Dec 02 12:52:41 CST 2013----message2Mon Dec 02 12:52:41 CST 2013----message3

我们可以使用偏应用函数优化以上方法,绑定第一个 date 参数,第二个参数使用下划线替换缺失的参数列表,并把这个新的函数赋给变量。

如下:

import java.util.Dateobject Test {   def main(args: Array[String]) {      val date = new Date      val logWithDateBound = log(date, _ : String)      logWithDateBound("message1" )      Thread.sleep      logWithDateBound("message2" )      Thread.sleep      logWithDateBound("message3" )   }   def log(date: Date, message: String)  = {     println(date + "----" + message)   }}

8.1if 语句

if 语句有布尔表达式及之后的语句块组成。

语法

if 语句的语法格式如下:

if(布尔表达式)
{
   // 如果布尔表达式为 true 则执行该语句块
}

如果布尔表达式为 true 则执行大括号内的语句块,否则跳过大括号内的语句块,执行大括号之后的语句块。

实例

object Test {
   def main(args: Array[String]) {
      var x = 10;

      if( x < 20 ){
         println("x < 20");
      }
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
x < 20

函数柯里化(Function Currying)

柯里化指的是将原来接受两个参数的函数变成新的接受两次一个参数的函数的过程。

如下,首先我们定义一个函数:

def add(x:Int,y:Int)=x+y

那么我们应用的时候,应该是这样用:add调用方法.
现在我们把这个函数变一下形:

def add = x + y

我们使用add,最后结果都一样是3,这种方式就叫柯里化。

8.2if...else 语句

if 语句后可以紧跟 else 语句,else 内的语句块可以在布尔表达式为 false 的时候执行。

语法

if...else 的语法格式如下:

if(布尔表达式){
   // 如果布尔表达式为 true 则执行该语句块
}else{
   // 如果布尔表达式为 false 则执行该语句块
}

实例

object Test {
   def main(args: Array[String]) {
      var x = 30;

      if( x < 20 ){
         println("x 小于 20");
      }else{
         println("x 大于 20");
      }
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
x 大于 20

Scala 闭包

闭包是一个函数实现方式,返回值依赖于声明在函数外部的一个或多个变量。

完整实例:

object Test {     def main(args: Array[String]) {        println( "muliplier value = " +  multiplier        println( "muliplier value = " +  multiplier     }     var factor = 3     val multiplier =  => i * factor  }  

是其实和js的闭包一样.

8.3if...else if...else 语句

if 语句后可以紧跟 else if...else 语句,在多个条件判断语句的情况下很有用。

语法

if...else if...else 语法格式如下:

if(布尔表达式 1){
   // 如果布尔表达式 1 为 true 则执行该语句块
}else if(布尔表达式 2){
   // 如果布尔表达式 2 为 true 则执行该语句块
}else if(布尔表达式 3){
   // 如果布尔表达式 3 为 true 则执行该语句块
}else {
   // 如果以上条件都为 false 执行该语句块
}

实例

object Test {
   def main(args: Array[String]) {
      var x = 30;

      if( x == 10 ){
         println("X 的值为 10");
      }else if( x == 20 ){
         println("X 的值为 20");
      }else if( x == 30 ){
         println("X 的值为 30");
      }else{
         println("无法判断 X 的值");
      }
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
X 的值为 30

Scala 字符串

实例:

object Test {   val greeting: String = "Hello,World!"   def main(args: Array[String]) {      println( greeting )   }}

以上实例定义了变量 greeting,为字符串常量,它的类型为 String (java.lang.String)。
在 Scala 中,字符串的类型实际上是 Java String,Scala本身没有 String 类。

在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象。

本文由威尼斯国际官方网站发布于电脑知识,转载请注明出处:一篇入门,语法基础

关键词: