第1章 认识Scala程序

本文来自艾叔编著的《零基础快速入门Scala》免费电子书,添加文末艾叔微信,获取完整版的PDF电子书
第1章  认识Scala程序

本章从宏观的角度介绍Scala代码,让大家能够对Scala程序建立一个初步印象。本章以HelloWorld为例,介绍Scala代码的基本架构,同时对Scala的语法特性做一说明。
1.1  Scala语言简介

Scala是一种特性丰富、功能强大的编程语言,具体描述如下:

    Scala结合了面向对象和函数式编程的特性;
    Scala是强类型(Strong Typing)语言,它不支持类型的隐式转换;
    Scala是静态(Static)类型的语言,这个特性有助于减少复杂应用程序中的错误;
    Scala程序在JVM上运行,可以利用Java丰富的库资源;
    Scala支持交互式解释器REPL((Read-Eval-Print-Loop,“读取-求值-输出”循环),可以直接在REPL上输入Scala语言,后台自动编译、运行,显示结果,可以实现程序的快速验证。

可以说,现有主流编程语言的特性在Scala中都可以看到。这对于经验丰富开发者来说,是一个很大的好处,各种特性可以统一在一门语言中,非常方便。但是,对于初学者来说,学习门槛高,内容庞杂,因此,学习曲线会比较陡峭,不少初学者在接触Scala后望而却步,但随着学习的深入,学习者会越来越体会到Scala带来的便利和强大功能,以及语言高度简洁所带来的魅力,就如同品一杯好茶,入口苦涩,之后却会回甜。

& Scala代码编译后,生成字节码在JVM上运行,因此Scala程序可以利用Java所有的库资源。
1.2  Scala语言的特点

我们都说Scala是一门个性十足的语言,那么他和我们常用的C/C++/Java这些编程语言相比,有什么特点呢?具体说明如下。

    支持面向对象

Scala支持面向对象,在Scala上完全可以像C++/Java那样,使用面向对象的特性;

    兼容Java库

Scala兼容Java库,Scala程序是基于JVM运行的,Scala代码编译后,输出文件是可以在JVM上运行的字节码,因此,它与丰富的Java库是完全兼容的,在Scala代码中,可以无缝调用Java库所提供的接口;

    支持REPL工具

Scala有一个REPL工具,REPL是read-eval-print-loop的缩写,因为它实现了Scala代码的编译、运行、显示,不断循环。正是有了这样一个工具,调试Scala代码变得非常直接和方便,常规的编译型语言,即便只需要在代码中改动一个字符,也需要重新编辑代码、重新编译代码、重新运行程序,有了REPL,直接在控制台上输入代码,回车后就可以看到结果。正是这样的便利性,Spark选择了Scala作为其开发语言,整个Spark框架就是采用Scala语言开发的;

    支持函数式编程

Scala支持函数式编程,所谓函数式编程,是相对命令式编程而言的,典型的命令式编程语言,如C语言,虽然是一种高级语言,对计算机进行了抽象,但是,它还是一种非常接近计算机底层的语言,C语言无论是从语法、编程思维都接近于更底层的汇编语言,而汇编语言经过汇编器,就成为机器指令了,因此,一条C语言语句,从语义上讲,是可以最终直接映射为一组机器指令的,因此,C程序可以认为是由一条条指令组合而成,这就是命令式编程语言的特点;而函数式编程语言,对于开发者来说,它看不到了底层的计算机,它看到的是一个个函数,利用函数的变换、推理来实现最终的功能,函数是实现程序功能的最小单元,因此,函数式编程语言中,其程序是由一个个函数组成的。Scala为了支持函数式编程,将函数列为Scala的第一要素,提供了很多的便利来使用函数,例如函数的定义、传递、使用等非常方便、函数可以声明为变量,可以像变量一样传参,还可以使用匿名函数等等;

    语法简洁,功能强大

Scala的语法非常简洁,同样的功能,如果使用Java实现,可能需要100行,而使用Scala可能就只要10行,代码的高度简洁,带来了代码的优雅,这也是很多人接触Scala后爱不释手的原因。Scala代码的简洁原因,主要是利用了Scala编译器的智能推断,它能根据已有的信息,尽可能地推断出其它信息,从而省去在代码中加入这些冗余的信息,例如,它可以根据函数的返回值,推断返回值类型,这样,在定义函数时,就不需要事先声明函数返回值类型了。
1.3  Scala对于初学者的特点

1.2节描述了Scala相对其他编程语言的一些特点,凡事有利也有弊,Scala的这些特性也有不好的地方,尤其对于初学者,总结如下。

1)学习曲线陡峭,因为Scala特性太多,语法糖非常多,高度抽象,这些对于初学者来说,往往会无所适从,不知从何学起,或者抓不到重点,从而导致效果欠佳,从而影响进一步学习;

2)协作效率不高,因为Scala太灵活,为开发者提供了更多的选择,同样是Scala,不同的开发者写出的代码,可能完全看不懂,这个不像Java,这是因为没有那么灵活,这样大家在风格、编写上更容易统一;

3)程序运行效率差异大,Scala为开发者提供了很大的便捷性,从而也向开发者屏蔽了许多的细节,同样的一个功能,Scala可能有10种实现方法,但这10种方法的效率可能有上百倍的差异,因此,Scala程序的效率高度依赖于Scala开发者的经验。

总之,Scala是一门非常强大的语言,当我们掌握后,会变得非常喜欢它,再回过头看其它语言,更能感觉到Scala的魅力和优雅,但前提是,我们要有足够的耐心,能够敲开进入Scala的大门。
1.4  Scala程序代码架构

典型的Scala程序代码,如下面的HelloWorld.scala代码所示。

      1 package examples.idea.scala

      2

      3 import scala.collection.mutable.ArrayBuffer

      4

      5 object HelloWorld {

      6

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

      8     val bigList = new ArrayBuffer[Int]

      9     println("Hello World! Size " + bigList.size)

     10   }

     11 }

以上面的HelloWorld代码为例,一个典型的Scala程序代码通常包括4部分的内容:

    第一部分:package声明(代码第一行)

package声明用来表明该文件中的class/obect所在的package。

      1 package examples.idea.scala

package是关键字,这是固定的,examples.idea.scala是package信息,这是需要开发者确定的,全部小写,以点(.)作为分隔。

那么,什么是package呢?

我们知道,在一个project中,所有源码应该分类、分层存储,以本书为例,书中有很多示例代码和实验代码,如果放在一个目录下,逻辑混乱且容易出错,自然地,可以用一个examples目录存放所有示例代码;这些示例程序中,其中一部分示例程序是采用IDEA开发的,而另一部分是直接用VIM开发的,因此,可以用一个idea目录来存放所有采用IDEA开发的程序代码,用一个vim目录来存放所有采用VIM开发的程序代码;此外,这些示例程序都是用Scala开发的,因此可以再使用一个scala目录用于和其他语言所开发的程序做区分。因此,本书代码组织结构如下所示:

examples/idea/scala

examples/vim/scala

HelloWorld.scala是用IDEA开发的,同时又是一个scala示例代码,因此,它的路径是:examples/idea/scala/HelloWorld.scala。将examples/idea/scala中的斜杠用点(.)替换,变成examples.idea.scala的形式,这个就是package信息,然后,写入HelloWorld.scala。

因此,package实际上就是代码的组织信息(整个代码的组织结构又叫名字空间 Name Space)。

那么,package有什么用呢?

实际开发中,首先要确定的就是package,package一旦确定,代码的组织结构就确定了,以此分工会很方便,每个开发者拿到任务后,先使用IDEA创建package(再创建class/object),IDEA会根据Package信息,在src下自动创建对应的目录,例如examples.idea.scala就会创建examples/idea/scala,这样,每个开发者的源码目录下,就会有事先约定好的,固定的代码目录结构,开发结束后,代码合并会很方便,不会有冲突。

package信息写入Scala代码,可以确保编译后,生成固定的,同代码组织结构一致的目录,例如,HelloWorld.scala的Package是examples.idea.scala,那么,编译后,会在输出目录下(自己指定,不是src),创建examples/idea/scala,HelloWorld.class保存在此目录下。这样,多个开发者各自编译的输出,可以按照事先确定好的package,重新组织到一起,而不冲突。

在运行Scala程序时,指定Main class时,前面要加上package,例如:examples.idea.scala.HelloWorld,Java会按照examples/idea/scala这个相对路径,到指定的jar包,或搜索路径下查找HelloWorld.class是否存在,如果存在,则执行。因此,即使在一个程序中,出现了同名的HelloWorld.scala,由于package不一样,也是没有问题的。

同样的,在Scala代码中,如果要引用class,要使用import来引入该class所在的package信息。

因此,package的作用是:它提供了一种手段,可以事先确定代码(输出)的组织结构,从而,方便地实现开发者们的分工与合作。

    第二部分:import声明(代码第三行)

这个部分用来声明代码中所用到的class/object的package信息,例如,第8行使用了ArrayBuffer,它的pakcage是scala.collection.mutable,因此,需要在此处声明。

   3 import scala.collection.mutable.ArrayBuffer

& 可以使用下划线_来代表该package下的所有class,例如,scala.collection.mutable._,就表示scala.collection.mutable下的���有class,自然也包括ArrayBuffer。这样做的好处是,省事,如果有多个class在同一个package下,直接用_表示所有的class,比较方便。但要注意的是,_不能嵌套,例如使用scala.collection._,只能表示package为scala.collection的所有class,而scala.collection.mutable.ArrayBuffer是没有包含在里面的;

& 同一个package下的class,可以用一行import表示,class都放在一个大括号{}内,使用逗号隔开,例如:import scala.collection.mutable.{ArrayBuffer,Buffer};

& import scala.collection.mutable.{ArrayBuffer => MyBuffer}  // 引入class ArrayBuffer ,并将其重命名为MyBuffer,可以解决class重名冲突(如果不这样做,那就要在代码中,凡是涉及重名class的地方,都在class前面加上package信息,例如:import scala.collection.mutable.ArrayBuffer,而不能只写成ArrayBuffer,这样做很麻烦,且容易出错);

& 在IDEA中,可以使用Alt + Enter,对某个class/object,进行import自动补全,例如,将光标放到ArrayBuffer上,按下Alt + Enter,就会自动生成import scala.collection.mutable.ArrayBuffer;

& Scala中scala 和 java.lang packages以及object Predef是默认被import的,因此不需要在代码中再import;

& 此外,import的位置可以是在代码中间(在用到class/object的代码前面即可),不一定像Java那样,出现在代码的最前面。

    第三部分:object声明(代码第五行)

Scala的入口函数main和Java的入口函数一样,需要是个静态方法,不需要实例化对象,就可以被直接调用。因此,第三部分的作用是,实现main为静态方法。

& 面向对象中,有类和对象两个概念,类是抽象的,对象是具体的。以人为例子,人是一个抽象概念,是类(它描述了人的共同特点),而大街上看到的男男女女,每个个体,就是一个对象;

& 一般来说,方法都是在类中声明的,如果要使用一个方法,则要先对类实例化,创建一个对象,然后通过此对象来调用方法,面向对象中,不需要实例化,可以直接调用的方法,称为静态方法;

& Scala的入口函数和Java的入口函数,都要求是静态方法;

& 方法和函数在此混用,不做区别,但它们实际上是有区别的,后续会进行说明。

Scala支持面向对象,它使用class表示类,使用object表示是由系统自动创建的对象。例如下面的object HelloWorld,我们在代码中就可以把HelloWorld当成一个对象直接引用,HelloWorld中声明了一个main方法,可以直接调用HelloWorld.main,基于此,main就如同Java中的静态方法(Java中使用static来实现),不需要实例化(实际上是系统自动实例化了对象)就可以调用。

     5 object HelloWorld {

& object和class的区别:class是抽象的,object是由系统自动创建好的一个对象;

& object是系统自动创建的,例如 object HelloWorld,写了以后,在代码中就可以将HelloWorld作为一个对象的名字,直接引用,这个对象的创建过程是隐藏的,是由系统来完成的。而val bigList = new ArrayBuffer[Int]中,bigList也是一个对象,但这个对象是在代码中通过new创建的,创建过程是显式的,HelloWorld和bigList都是对象;

& object翻译成英文是对象,但是,在Scala中,对象和object是不等同的,它们的区别是:对象是指class实例化后的结果,object是系统自动实例化的结果,它是一个对象,除此外,还可以在代码中使用new创建对象:val bigList = new ArrayBuffer[Int],因此,Scala中,对象包括object和代码中创建的对象。

& object在Scala中,通常用来实现静态成员(Java中使用static来实现),例如静态成员变量,静态方法,Java中使用static修饰词来实现同样的功能;

& 总之,在编写一个Scala程序时,main方法一定要在一个object中。

    第四部分:main方法(代码第7~10行)

这部分是Scala程序的核心,Scala程序就是从main方法开始,一行行顺序执行,直到最后退出main方法。

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

      8     val bigList = new ArrayBuffer[Int]

      9     println("Hello World! Size " + bigList.size)

     10   }

第7行是函数的声明部分,def是关键字;main是方法名;()括号内部是参数信息,只有1个参数args,args是参数变量名,冒号:是分隔符,左侧为变量名,右侧为参数类型,args的类型是Array[String],即字符串数组,命令行中传入的程序参数,就存储在这个数组中,按照从左到右的顺序,依次为args(0),args(1)等;同样的,第二个冒号:的左侧是方法名、参数等信息,右侧为返回值类型,此处的类型为Unit,Unit是Scala中的一种特殊数据类型,它的值是一个括号(),编译器会在函数结束位置自动插入此返回值(本例中,插入到第9行代码后面),因此,不需要我们在代码中设置显式的返回值,因此,凡是返回值为Unit的方法,都不需要关注返回值,但这并不意味着没有返回值;此外,Unit右侧是有个等于号=的,意思是:将大括号内的函数体,赋值给左侧定义的main函数变量,=要加上,否则会报语法错误;

第8~9行,是main函数体的代码,程序从第8行开始,一行行向下执行,当然,如果有控制结构,如:if、while、for等,则按照其逻辑,跳转到相应的行,否则,就是一行一行,顺序向下执行,绝不会直接跳过某行,而去执行其它行的代码。此外,每行后面没有分号(C/C++/Java每行后面,需要用分号;作为一行的分隔),Scala是不需要的,当然,在第8行末尾加上分号,也不会报错,但不建议这样做。

总结

    一个Scala程序包括:package声明、import声明、object、main方法及实现,四个部分;
    package在编写程序前,要慎重确定,要充分考虑如何组织便于分工和合作;
    函数的执行从main方法开始,传递给程序的参数保存在args:Array[String]内,每个参数都是String类型,后续可以根据自己的需要,将String转换成其它类型;
    main方法的返回值是Unit类型,Unit类型的值是由编译器自动插入的,因此,在代码中不需要考虑返回值的问题;
    main方法是静态方法,不需要在代码中创建对象进行调用,这是因为main包含在object之中,object是由Scala自动创建的对象。

添加艾叔微信,加入Linux(Shell+Zabbix)、大数据(Spark+Hadoop)、云原生(Docker+Kubernetes)技术交流群

 关注艾叔公众号,获取更多一手信息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值