scala学习手记1二威尼斯人6799.com,学习笔记

再而三上学,那1篇首假诺由此scala来奚弄java的,同样是jvm上的语言,差别咋就这么大啊?

Scala与Java的关系… 4

在上壹节创制了多少个scala类,如若未有越来越多的主意,scala类的概念还足以更简便易行壹些,看一下上面这一个CreditCard类的定义:

作为一个有.NET开发经历的程序员,当初刚接触java时,相信广大人对java语言有以下不爽(只列了十分小片段):

安装Scala.
4

class CreditCard(val number: Int, var creditLimit: Int)
  1. 一堆的setter/getter方法,没有c#中的property属性概念

  2. 格局的参数值,不可能安装缺省值

  3. 不定个数参数的写法太单纯

Scala解释器的施用…

正确,只用1行就形成了类的概念,连大括号都不须求。

宣示变量…
5

因为scala也是运作在JVM上,能够怀想以java的不二等秘书籍来探视编写翻译后的类公事。查看的方式依旧相比灵敏的,能够选用JD-GUI,也得以使用javap
–private
CreditCard命令,还有二个在线反编译的网址ShowMyCode。反编写翻译后的Java代码:

下一场java的跟随者讲出一群大道理,说这么设计是怎么如何有道理,各样洗脑,时间长了,也就被迫习惯了。要不是遇到scala,作者还真就信了,你看看人家scala同学,二〇〇一/200四公布的,早就把那几个全实现了,而java同学作为jvm上的泰斗,那个年一贯执迷不悟,不思进取,已经被jvm上的别的同学远远甩在前边了,java你可长点心吧!进入正题,直接上码:

数据类型与操作符…

public class CreditCard {  public int number() {    return number;  }  public int creditLimit() {    return creditLimit;  }  public void creditLimit_$eq(int x$1) {    creditLimit = x$1;  }  public CreditCard (int number, int creditLimit) {    this.number = number;    this.creditLimit = creditLimit;    super ();  }  private final int number;  private int creditLimit;}

一、参数缺省值

函数调用与apply()函数…

好长的java代码。首先scala默许将CreditCard类转换为了public。因为在Credit卡德.scala上将number申明为val,所以在反编写翻译生成的java代码中,number被定义为final。其余在编写翻译后的代码中还足以看出三个构造器以及读写成员变量的主意。能够看到成员变量的getter和setter与大家在java中习惯使用的命名形式有点不平等。别的由于number有final修饰符,由此就不曾它的setter方法。假诺scala中的成员变量的定义符号既不是var也不是val,那Scala就会为之创设一个private字段以及private
getter和setter方法,也为此不可能在类外部访问那些成员变量了。

  /**
   * 参数缺省值
   * @param person
   * @param msg
   */
  def saySomething(person: String = "somebody", msg: String = "Hello") = {
    println(person + " say : " + msg);
  }

if表达式…
6

放置类定义中的全体可执行讲话或表明式都会被视为类的构造器的组成都部队分。上边包车型大巴代码正是二个示范:

 调用示例:

语句终结符、块表明式…

class Sample {  println("You are constructing an instance of Sample")}new Sample
    saySomething()
    saySomething("jimmy")
    saySomething("jimmy", "hi")

输入和输出…

在这段代码中先定义了1个类Sample,随后再次创下办了1个萨姆ple类的实例,执行看一下:

当然那里有二个细微的界定,假设要用参数缺省值,建议具有的参数全安装缺省值,若是只给壹些参数设置缺省值,函数定义不会有标题,调用时,上面包车型地铁示范编写翻译就通可是了(大意是提供的参数不足等等),我们能够把msg参数的缺省值去掉再尝试。

循环… 7

威尼斯人6799.com 1

那么,最后编写翻译出来的class,到底是何许促成的啊?可以依靠一些反编写翻译工具,比如JD-GUI还原成java1看究竟:

高级for循环… 7

在成立实例的时候输出了类定义中的print语句,因为那段print语句是构造器的一部分。

    public void saySomething(String person, String msg) {
        Predef..MODULE$.println(new StringBuilder().append(person).append(" say : ").append(msg).toString());
    }

    public String saySomething$default$1() {
        return "somebody";
    }

    public String saySomething$default$2() {
        return "Hello";
    }

函数的概念与调用…

而外在主构造函数中提供成员变量,大家还是能够在类里面定义别的字段、方法、零个或多个副构造函数。在底下那个类中在类里面定义了1个成员变量position、1个副构造函数this()、并且重写了toString()方法。

也正是说,scala中的def saySomething(person: String = “somebody”, msg: String = “Hello”)
若是用java实现的话,能够用三个法子来变相完毕,每一种缺省参数,也便是多个独门的版本,换言之,在编写翻译器层面,其实java的编写翻译器假设想做,是完全可以形成的,为啥不做?懒!顽!

在代码块中定义包罗多行语句的函数体…

class Person(val firstName: String, val lastName: String) {  private var position: String = _  println("Creating " + toString  def this(firstName: String, lastName: String, positionHeld: String) {    this(firstName, lastName)    position = positionHeld  }  override def toString(): String = {    firstName + "" + lastName + " holds " + position + " position "  }}val john = new Person("John", "Smith", "Analyst")printlnval bill = new Person("Bill", "Walker")println

二、class的property

递归函数与再次来到类型…

实践代码的结果如下:

/**
 * 定义一个带参主构造器的类
 * @param pReadOnly
 */
class Sample(pReadOnly: String) {

  /**
   * 可读写的属性
   */
  var myProperty: String = _;

  private val _readOnly: String = pReadOnly;

  /**
   * 只读属性
   */
  def readOnly: String = _readOnly;

}

暗中认可参数…

威尼斯人6799.com 2

调用示例:

函数调用时带名参数…

稍稍关切下副构造函数的兑现:倘使有主构造函数的话,那么副构造函数的第二行必须是主构造函数可能别的副构造函数的调用。那或多或少倒是和java继承父类时有点相似。

    val sample = new Sample("test")
    println(sample.readOnly)
    sample.myProperty = "a new value"
    println(sample.myProperty)

变长参数…

除此以外还值得注意的正是成员变量position的概念,把那1行单独拎出来看看吧:

没了setter/getter看起来倍儿清爽!照旧反编写翻译class看看:

队列作为变长参数…
9

private var position: String = _
public class Sample
{
  private String myProperty;
  private final String _readOnly;

  public String myProperty()
  {
    return this.myProperty; } 
  public void myProperty_$eq(String x$1) { this.myProperty = x$1; } 
  private String _readOnly() {
    return this._readOnly;
  }

  public String readOnly()
  {
    return _readOnly();
  }

  public Sample(String pReadOnly)
  {
    this._readOnly = pReadOnly;
  }
}

过程… 9

首先比较有意思的是发轫化赋值,赋值是一个“_”——下划线。在这里“_”代表相应品种的默许值。对于Int,它的值是0;对于Double,它的值是0.0;对于String,它的值正是null。通过利用“_”,能够很有益地为var成员变量设置初叶暗中认可值。可是不可能为val成员运用“_”,因为val成员差别意修改,所以必须显式钦点起初值

能够看到,myProperty自动生成了setter/gettter,如故是在编译器层面,就足以随手做掉的事体,java编写翻译器依然不肯做。

lazy值…
9

透过翻看Person.scala的字节码反编译出来的java类,能够看到scalac编译器为成员变量position暗中同意设置了getter和setter方法(即使从未依照大家习惯的JavaBean的主意实行安装)。scala中定义的成员变量的可知性在反编写翻译出来的Java代码中由getter和setter方法的造访权限来控制。

3、不定个数参数值

异常… 10

一经更欣赏古板的JavaBean式的诠释,能够在成员变量定义时添加注明@BeanProperty:

那几个难点,java中即使能够xxx(String[]
args)用数组传递达到近似的意义,可是固然传贰个空数组,也至少也得写一个xxx(null)吧,既然此时参数都为空了,为何不直接xxx()更加直接,看看scala:

Array.
10

@BeanProperty  var position: String = _
  /**
   * 不固定个数的参数
   * @param x
   * @return
   */
  def add(x: Int*) = {
    var i = 0
    for (j <- x) i += j
    i
  }

ArrayBuffer.
11

声称前记得导入评释。可是那也有少数限制:此时成员变量不可再声称为private。而且这么做只是会再额外生成多少个JavaBean式的getter和setter,原来的getter和setter也会继续保存。那足以在编译Person类今后再用javap
–private验证一下:

 调用:

遍历Array和ArrayBuffer.
11

威尼斯人6799.com 3

    println(add())
    println(add(1, 2, 3, 4, 5))

数组常见操作…
1壹

正是这么。

 鲜明的更加高端大气上档次,继续反编写翻译,那些要略复杂点:
先是生成了那般1个类:

行使yield和函数式编制程序转换数组…
12

#######

public final class DefHello$$anonfun$add$1 extends AbstractFunction1.mcVI.sp
  implements Serializable
{
  public static final long serialVersionUID = 0L;
  private final IntRef i$1;

  public final void apply(int j)
  {
    apply$mcVI$sp(j); } 
  public void apply$mcVI$sp(int j) { this.i$1.elem += j; }


  public DefHello$$anonfun$add$1(IntRef i$1)
  {
  }
}

算法案例:移除第二个负数之后的拥有负数…
12

然后是:

算法案例:移除第二个负数之后的具有负数(勘误版)…
13

   public void main(String[] args)
    {
        Predef..MODULE$.println(BoxesRunTime.boxToInteger(add(Nil..MODULE$)));
        Predef..MODULE$.println(BoxesRunTime.boxToInteger(add(Predef..MODULE$.wrapIntArray(new int[] { 1, 2, 3, 4, 5 }))));
        ...
    }

    public int add(Seq<Object> x)
    {
        IntRef i = IntRef.create(0);
        x.foreach(new AbstractFunction1.mcVI.sp() { public static final long serialVersionUID = 0L;

            public final void apply(int j) { apply$mcVI$sp(j); }
            public void apply$mcVI$sp(int j) { DefHello..this.elem += j; }

        });
        return i.elem;
    }

创建Map.
13

提起底调用时,add()那里固然scala没有传任何参数,但从反编写翻译结果上看,最后照旧成为了add(Nil..MODULE$)),编写翻译器自动加了1个参数,以满足java的正经。

访问Map的元素…
13

四、泛型早先

修改Map的元素…
13

java中的泛型是2个”伪”泛型,其品种擦除机制只是障眼法而已,因而带来了过多应用上的限量,比如上边这些例子:

遍历Map.
14

public class SampleClass<T> {
    private T _t;

    public SampleClass(T t) {
        this._t = t;
    }

    public T getT() {
        return _t;
    }
}

SortedMap和LinkedHashMap.
14

 那里定义了三个泛型类,若是想创建贰个此类的数组:

Map的成分类型—Tuple.
1四

SampleClass<String>[] objs = new SampleClass<String>[10];

概念一个不难易行的类…
1四

编写翻译器会一贯报错:Error: java: generic array creation,原因是:type
erase后,内部已经是SampleClass[],按OOP的尺度,能够提升转型为Object[],那下可好了,Object是万能类型,若是向这些万能类型的数组里参与二个不是萨姆pleClass<String>的实例,理论上也是同意的,那就违背了泛型约束的初衷。

getter与setter.
15

唯独在scala中,却是可以如此做的,看上面包车型客车代码:

自定义getter与setter.
15

class MyClass[T](t1: T) {
  var t: T = t1;
}

仅暴露field的getter方法… 15

然后能够如此用:

private[this]的使用…
16

    val objs = new Array[MyClass[String]](10)
    objs(0) = new MyClass[String]("a")
    for (x <- objs; if x != null) println(x.t)

Java风格的getter和setter方法…
16

编写翻译和平运动作一切平常,那是何许情况?还是反编写翻译解密:

辅助constructor.
16

    MyClass[] objs = new MyClass[10];

    objs[0] = new MyClass("a");

    Predef..MODULE$.refArrayOps((Object[])objs).withFilter(new DefHello..anonfun.main.1()).foreach(new DefHello..anonfun.main.2());

主constructor.
17

原本,对于java的伪泛型机制,scala早就看透了这全体,因而它应用了1种略带”极端”的做法,直接动用原始类型,凶残的对java的泛型机制回应:『不约,我们不约』。

内部类…
18

刺探以上这几个后,作者只得特别敬佩坚定不移利用java语言写出这么多NB开源框架的达人们,硬是用二个要吗啥未有的言语为开源世界做出这样大的进献,那是1种何等的精神,无禁让自家想起了《道士下山》中猿击术中的精髓:”不离不弃,不嗔不恨!”,作者只想说:这么长年累月,你们是怎么忍下来的!

object.
18

 

伴生对象…
1玖

So,Scala既然那样好,就宏观无缺了么?当然不是,成效越强大,语法越灵活,自然学习开支也更加高。此外,品质方面,它生成的字节码感觉比java略多,网上有过多关于scala与java的性质研讨,包蕴google也有近似的估测,有人说那二者大致,然则多数人要么觉得在jvm上,scala的性格完全来看要低于java,只好达到java的8成左右(详情可机关百度,有好多这类小说)

让object继承抽象类…
1玖

apply方法…
19

main方法…
20

用object来完成枚举功用…
20

extends.
21

override和super.
21

override field.
21

val、var override/实现 def.
23

isInstanceOf和asInstanceOf.
24

getClass和classOf.
25

行使形式相称实行项目判断…
25

protected.
25

调用父类的constructor.
26

匿名内部类…
二陆

抽象类…
27

抽象field.
27

将trait作为接口使用…
2八

在Trait中定义具体方法…
28

在Trait中定义具体字段…
2九

在Trait中定义抽象字段…
2九

为实例混入trait.
2九

trait调用链…
30

在trait中覆盖抽象方法…
30

混合使用trait的具体方法和虚幻方法…
3壹

trait的结构机制…
3一

trait 田野的发轫化…
3贰

trait继承class.
33

将函数赋值给变量…
3四

匿名函数…
3肆

高阶函数…
3四

高阶函数的项目估算…
35

Scala的常用高阶函数…
3伍

闭包… 36

SAM转换… 36

Currying函数…
36

return到外围函数…
三七

Scala的集结种类布局…
37

List.
37

LinkedList.
38

Set.
38

聚拢的函数式编制程序…
38

函数式编制程序综合案例:计算多个文件内的单词总数…
3玖

方式相配…
3玖

在格局相配中央银行使if守卫…
40

在情势相配中展开变量赋值…
40

对项目进行形式相称…
40

对Array和List实行方式相配…
四一

case class与形式相配…
四一

Option与情势相配…
4二

连串参数…
42

泛型类…
42

泛型函数…
43

上边界Bounds.
43

下边界Bounds.
44

View Bounds.
45

Context Bounds.
46

Manifest Context Bounds.
46

协变和逆变…
4七

Existential Type.
47

隐式转换…
4捌

动用隐式转换抓牢现有类型…
4九

隐式转换函数作用域与导入…
4玖

隐式转换的产生时机…
4玖

隐式参数…
50

Actor.
50

Actor的始建、运维和新闻收发…
50

收发case class类型的音讯…
5一

Actor之间互相收发音讯…
5一

联合新闻和Future.
52

   

Scala与Java的关系

Scala与Java的关联是可怜连贯的!!

因为Scala是基于Java虚拟机,也正是JVM的一门编制程序语言。全体Scala的代码,都急需通过编写翻译为字节码,然后交由Java虚拟机来运维。

因此Scala和Java是足以无缝互操作的。Scala可以随心所欲调用Java的代码。所以Scala与Java的涉及是十二分越发严密的。

安装Scala

·从Scala官方网址下载,http://www.scala-lang.org/download/,windows版本的安装包是scala-2.11.7.msi。

·使用下载下来的安装包安装Scala。

·在PATH环境变量中,配置$SCALA_HOME/bin目录。

·在windows命令行内即可直接键入scala,打开scala命令行,实行scala编制程序。

Scala解释器的施用

·REPL:Read(取值)->
伊娃luation(求值)->
Print(打印)->
Loop(循环)。scala解释器也被誉为REPL,会连忙编译scala代码为字节码,然后交到JVM来施行。

·总计表明式:在scala>命令行内,键入scala代码,解释器会直接回到结果给您。假如您没有点名变量来存放这么些值,那么值暗中认可的称谓为res,而且会议及展览示结果的数据类型,比如Int、Double、String等等。

·例如,输入1

  • 1,会看到res0:
    Int = 2

·内置变量:在后头能够一而再运用res这几个变量,以及它存放的值。

·例如,2.0
* res0,返回res1: Double =
4.0

·例如,”Hi,
” + res0,返回res2:
String = Hi, 2

·自动补全:在scala>命令行内,能够行使Tab键进行自动补全。

·例如,输入res二.to,敲击Tab键,解释器会展现出以下选项,toCharArray,toLowerCase,toString,toUpperCase。因为那时无法看清你须要补全的是哪贰个,由此会提必要你持有的选项。

·例如,输入res二.toU,敲击Tab键,直接会给你补全为res二.toUpperCase。

声称变量

·声明val变量:能够表明val变量来存放表明式的计量结果。

·例如,val
result = 1 + 1

·后续那么些常量是足以继续采纳的,例如,二
* result

·但是常量阐明后,是无力回天更改它的值的,例如,result
= 壹,会再次来到error:
reassignment to val的错误音信。

·声明var变量:借使要证明值可以转移的引用,能够选取var变量。

·例如,val
myresult = 1,myresult
= 2

·不过在scala程序中,平常提出选取val,也便是常量,因而比如类似于spark的巨型复杂系统中,须要大量的网络传输数据,就算使用var,恐怕会担心值被漏洞相当多的更改。

·在Java的大型复杂系统的布署和付出中,也应用了接近的特点,我们1般会将传递给任何模块
/ 组件 / 服务的对象,设计成不足变类(Immutable Class)。在里头也会使用java的常量定义,比如final,阻止变量的值被改动。从而增强系统的健壮性(robust,鲁棒性),和安全性。

·内定项目:甭管注脚val变量,照旧表明var变量,都能够手动钦赐其连串,假若不钦命的话,scala会自动依照值,进行项指标估测计算。

·例如,val
name: String = null

·例如,val
name: Any = “leo”

·注脚多少个变量:能够将多少个变量放在壹块儿开始展览宣示。

·例如,val
name1, name2:String = null

·例如,val
num1, num2 = 100

数据类型与操作符

·基本数据类型:Byte、Char、Short、Int、Long、Float、Double、Boolean。

·乍1看与Java的主导数据类型的卷入档次相同,可是scala未有基本数据类型与包装档次的概念,统1都以类。scala本人会负责基本数据类型和引用类型的变换操作。

·使用上述项目,直接就能够调用多量的函数,例如,一.toString(),壹.to(十)。

·类型的抓牢版类型:scala使用过多增进类给数据类型扩展了不少种进步的效应或函数。

·例如,String类通过StringOps类增加了多量的函数,”Hello”.intersect(” World”)。

·例如,Scala还提供了RichInt、RichDouble、RichChar等类型,RichInt就提供了to函数,一.to(10),此处Int先隐式转换为RichInt,然后再调用其to函数

·基本操作符:scala的算术操作符与java的算术操作符也未有啥样界别,比如+、-、*、/、%等,以及&、|、^、>>、<<等。

·但是,在scala中,那么些操作符其实是数据类型的函数,比如一

  • 一,能够写做1.+(一)

·例如,一.to(十),又足以写做1to 拾

·scala中没有提供++、–操作符,大家不得不利用+和-,比如counter =
一,counter++是不对的,必须写做counter
+= 1.

函数调用与apply()函数

·函数调用方式:在scala中,函数调用也很简单。

·例如,import
scala.math._,sqrt(2),pow(2,
4),min(3, Pi)。

·分化的某个是,假使调用函数时,不须要传递参数,则scala允许调用函数时省略括号的,例如,”Hello
World”.distinct

·apply函数

    ·Scala中的apply函数是可怜出格的壹种函数,在Scala的object中,可以注解apply函数。而使用“类名()”(严刻来讲应该是“对象名()”)的花样,其实就是“类名.apply()”(严俊来讲应该是“对象名.apply()”)的壹种缩写。经常选取那种方式来构造类的靶子,而不是运用“new
类名()”的主意。

·例如,”Hello
World”(陆),因为在StringOps类中有def apply(n: Int):
Char的函数定义,所以”Hello
World”(陆),实际上是”Hello
World”.apply(陆)的缩写。

·例如,Array(一,
二, 三, 四),实际上是用Array
object的apply()函数来创制Array类的实例,也正是二个数组。

 

if表达式

·if表明式的概念:在Scala中,if表明式是有值的,正是if可能else中最终1行语句重返的值。

    ·例如,val age = 30;
if (age > 18) 1 else 0

    ·能够将if表明式赋予叁个变量,例如,val isAdult = if (age > 1八) 一 else 0

    ·此外1种写法,var
isAdult = -一; if(age > 1八) isAdult = 一 else isAdult = 0,不过一般使用上壹种写法

·if表明式的品种估摸:是因为if表明式是有值的,而if和else子句的值类型只怕差别,此时if说明式的值是怎么项目呢?Scala会自动实行测算,取多个品类的公共父类型。

    ·例如,if(age > 18)
1 else 0,表达式的品种是Int,因为一和0都以Int

    ·例如,if(age > 1捌)
“adult” else 0,此时if和else的值分别是String和Int,则表明式的值是Any,Any是String和Int的公物父类型

    ·即便if前面没有跟else,则私下认可else的值是Unit,也用()表示,类似于java中的void也许null。例如,val age = 1二;
if(age > 1捌) “adult”。此时就一定于if(age
> 18) “adult” else ()。

·将if语句放在多行中:暗中认可情形下,REPL只好说飞鹤行语句,不过if表明式平时供给放在多行。

    ·可以运用{}的法子,比如以下办法,大概使用:paste和ctrl+D的办法。

if(age > 18) { “adult”

} else if(age > 12) “teenager” else
“children”

语句终结符、块表达式

·暗许景况下,scala不需求语句终结符,暗许将每一行作为1个话语

·壹行放多条语句:假使1行要放多条语句,则必须利用语句终结符

    ·例如,使用分号作为语句终结符,var a, b, c = 0; if(a < 拾) { b = b + 一; c = c + 1}

    ·平常来说,对于多行语句,照旧会动用花括号的格局

if(a < 10) {

    b = b + 1

    c = c + 1

}

·块表明式:块表明式,指的便是{}中的值,当中能够分包多条语句,最终五个话语的值正是块表达式的再次来到值。

    ·例如,var d = if(a
< 10) { b = b + 1; c + 1 }

输入和出口

·print和println:print打字与印刷时不会加换行符,而println打字与印刷时会加多少个换行符。

    ·例如,print(“Hello
World”); println(“Hello World”)

·printf:printf能够用于实行格式化

    ·例如,printf(“Hi, my
name is %s, I’m %d years old.\n”, “Leo”, 30)

·readLine: readLine允许我们从控制台读取用户输入的数据,类似于java中的System.in和Scanner的功用。

·综合案例:游戏厅门禁

val name = readLine(“Welcome to Game House. Please
tell me your name: “)

print(“Thanks. Then please tell me your age:
“)

val age = readInt()

if(age > 18) {

  printf(“Hi, %s, you are %d years old, so you are
legel to come here!”, name, age)

} else {

  printf(“Sorry, boy, %s, you are only %d years old.
you are illegal to come here!”, name, age)

}

循环

·while do循环:Scala有while do循环,基本语义与Java相同。

var n = 10

while(n > 0) {

  println(n)

  n -= 1

}

·Scala未有for循环,只可以动用while替代for循环,可能选拔简易版的for语句

    ·简易版for语句:var n = 10;
for(i <- 1 to n) println(i)

    ·或许使用until,表式不达到上限:for(i <- 1 until n) println(i)

    ·也能够对字符串实行遍历,类似于java的增强for循环,for(c <-
“Hello World”) print(c)

·跳出循环语句

    ·scala未有提供类似于java的break语句。

    ·然而能够接纳boolean类型变量、return可能Breaks的break函数来顶替利用。

import scala.util.control.Breaks._

breakable {

    var n = 10

    for(c <- “Hello World”) {

        if(n == 5) break;

        print(c)

        n -= 1

    }

}

高级for循环

·多重for循环:玖⑨乘法表

for(i <- 1 to 9; j <- 1 to 9) {

  if(j == 9) {

    println(i * j)

  } else {

    print(i * j + ” “)

  }

}

·if守卫:取偶数

for(i <- 1 to 100 if i % 2 == 0) println(i)

·for推导式:构造集合

for(i <- 1 to 10) yield i

 

函数的定义与调用

在Scala中定义函数时,供给定义函数的函数名、参数、函数体。

咱俩的率先个函数如下所示:

def sayHello(name: String, age: Int) = {

  if (age > 18) { printf(“hi %s, you are a big
boy\n”, name); age }

  else { printf(“hi %s, you are a little boy\n”,
name); age

}

sayHello(“leo”, 30)

Scala供给必须付出全数参数的项目,但是不必然给出函数重返值的品种,只要左边的函数体中不带有递归的话语,Scala就足以团结依据左侧的表明式猜度出再次来到类型。

在代码块中定义包涵多行语句的函数体

单行的函数:def
sayHello(name: String) = print(“Hello, ” + name)

一经函数体中有多行代码,则能够行使代码块的法子包裹多行代码,代码块中最后一行的重临值就是总体函数的重回值。与Java中分歧,不是运用return重返值的。

例如如下的函数,实现拉长的效应:

def sum(n: Int) = {

  var sum = 0;

  for(i <- 1 to n) sum += i

  sum

}

递归函数与再次回到类型

要是在函数体内递归调用函数自身,则必须手动给出函数的归来类型。

譬如,完毕经典的斐波那契数列:

9 + 8; 8 + 7 + 7 + 6; 7 + 6 + 6 + 5 + 6 + 5 + 5 + 4;
….

def fab(n: Int): Int = {

  if(n <= 1) 1

  else fab(n – 1) + fab(n – 2)

}

暗许参数

在Scala中,有时大家调用有个别函数时,不期望交给参数的具体值,而愿意利用参数自己暗中认可的值,此时就定义在概念函数时行使暗中同意参数。

def sayHello(firstName: String, middleName: String =
“William”, lastName: String = “Croft”) = firstName + ” ” + middleName +
” ” + lastName

假若给出的参数不够,则会从左往右依次使用私下认可参数。

def sayHello(name: String, age: Int = 20) {

  print(“Hello, ” + name + “, your age is ” +
age)

}

sayHello(“leo”)

函数调用时带名参数

在调用函数时,也足以不依据函数定义的参数顺序来传递参数,而是采纳带名参数的不贰秘诀来传递。

sayHello(firstName = “Mick”, lastName = “Nina”,
middleName = “Jack”)

还是可以勾兑使用未命名参数和带名参数,不过未命名参数必须排在带名参数前面。

sayHello(“Mick”, lastName = “Nina”, middleName =
“Jack”)

变长参数

在Scala中,有时我们须求将函数定义为参数个数可变的花样,则此时得以行使变长参数定义函数。

def sum(nums: Int*) = {

  var res = 0

  for (num <- nums) res += num

  res

}

sum(1, 2, 3, 4, 5)

队列作为变长参数

在要是想要将二个已部分种类直接调用变长参数函数,是畸形的。比如val s = sum(① to 伍)。此时亟需选拔Scala特殊的语法将参数定义为体系,让Scala解释器能够辨识。那种语法万分管用!一定要特出主意,在spark的源码中大量地应用到了。

val s = sum(1 to 5: _*)

案例:使用递归函数完毕增进

def sum2(nums: Int*): Int = {

  if (nums.length == 0) 0

  else nums.head + sum2(nums.tail: _*)

}

过程

在Scala中,定义函数时,假诺函数体直接包裹在了花括号内部,而从未应用=连接,则函数的回来值类型正是Unit。那样的函数就被喻为进程,即经过就算从未再次回到值的函数。

进度还有1种写法,就是将函数的回来值类型定义为Unit。

def sayHello(name: String) = “Hello, ” +
name//函数

def sayHello(name: String) { print(“Hello, ” + name);
“Hello, ” + name }//有值,但未使用=号,照旧经过

def sayHello(name: String): Unit = “Hello, ” +
name//有值,有=号,但强制重返类型为空,则仍然经过

lazy值

在Scala中,提供了lazy值的表征,也等于说,假若将三个变量注脚为lazy,则唯有在率先次采纳该变量时,变量对应的表明式才会时有产生总结。那种特征对于尤其耗时的盘算操作特别有用,比如打开文件举行IO,进行互联网IO等。

 

import scala.io.Source._

lazy val lines =
fromFile(“C://Users//Administrator//Desktop//test.txt”).mkString

哪怕文件不设有,也不会报错,只有首先个使用变量时会报错,阐明了表明式总结的lazy性情。

 

val lines =
fromFile(“C://Users//Administrator//Desktop//test.txt”).mkString

lazy val lines =
fromFile(“C://Users//Administrator//Desktop//test.txt”).mkString

相当于概念了1个艺术,唯有在调用该方式时才会去实践方法体:

def lines =
fromFile(“C://Users//Administrator//Desktop//test.txt”).mkString

异常

在Scala中,万分处理和破获机制与Java是11分相似的。

try {

  throw new IllegalArgumentException(“x should not be
negative”)

} catch {

  case _: IllegalArgumentException =>
println(“Illegal Argument!”)

} finally {

  print(“release resources!”)

}

 

Import java.io._

try {

  throw new IOException(“io exception!!!”)

} catch {

  case _: IllegalArgumentException =>
println(“illegal argument”)

}

 

try {

  throw new IOException(“user defined
exception”)

} catch {

  case e1: IllegalArgumentException =>
println(“illegal argument”)

  case e2: IOException => println(“io
exception”)

}

Array

在Scala中,Array代表的意思与Java中好像,也是长度不得更改的数组。此外,由于Scala与Java都是运作在JVM中,双方能够相互调用,由此Scala数组的最底层实际上是Java数组。例如字符串数组在底层就是Java的String[],整数数组在尾部正是Java的Int[]。

// 数组初叶化后,长度就一定下来了,而且成分全体依据其项目初阶化

val a = new Array[Int](10)

a(0)

a(0) = 1

val a = new Array[String](10)

// 能够直接运用Array()创立数组,元素类型自动测算

val a = Array(“hello”, “world”)

a(0) = “hi”

val a = Array(“leo”, 30)

ArrayBuffer

在Scala中,要是必要接近于Java中的ArrayList那种长度可变的集合类,则足以行使ArrayBuffer。

// 倘若不想每回都施用全限定名,则足以先行导入ArrayBuffer类

import scala.collection.mutable.ArrayBuffer

// 使用ArrayBuffer()的章程能够成立1个空的ArrayBuffer

val b = ArrayBuffer[Int]()

// 使用+=操作符,能够加上3个成分,或然多个成分

// 那几个语法必须要谨记在心!因为spark源码里大批量用到了那种集合操作语法!

b += 1

b += (2, 3, 4, 5)

// 使用++=操作符,能够添加任何集合中的全数因素

b ++= Array(6, 7, 8, 9, 10)

// 使用trimEnd()函数,能够从尾巴部分截断钦赐个数的因素

b.trimEnd(5)

 

// 使用insert()函数能够在钦赐地方插入成分

// 不过那种操作成效相当低,因为急需活动钦命地方后的享有因素

b.insert(5, 6)

b.insert(6, 7, 8, 9, 10)

// 使用remove()函数能够移除钦命地方的要素

b.remove(1)

b.remove(1, 3)

// Array与ArrayBuffer能够相互开始展览更换

b.toArray

a.toBuffer

遍历Array和ArrayBuffer

// 使用for循环和until遍历Array / ArrayBuffer

// 使until是RichInt提供的函数

for (i <- 0 until b.length)

  println(b(i))

// 跳跃遍历Array /
ArrayBuffer

for(i <- 0 until (b.length, 2))

  println(b(i))

// 从尾部遍历Array /
ArrayBuffer

for(i <- (0 until b.length).reverse)

  println(b(i))

// 使用“增强for循环”遍历Array /
ArrayBuffer

for (e <- b)

  println(e)

数组常见操作

// 数组成分求和

val a = Array(1, 2, 3, 4, 5)

val sum = a.sum

// 获取数组最大值

val max = a.max

// 对数组实行排序

scala.util.Sorting.quickSort(a)

// 获取数组中具备因素内容

a.mkString

a.mkString(“, “)

a.mkString(“<“, “,”, “>”)

// toString函数

a.toString

b.toString

运用yield和函数式编制程序转换数组

// 对Array举办转移,获取的或然Array

val a = Array(1, 2, 3, 4, 5)

val a2 = for (ele <- a) yield ele * ele

// 对ArrayBuffer进行转移,获取的要么ArrayBuffer

val b = ArrayBuffer[Int]()

b += (1, 2, 3, 4, 5)

val b2 = for (ele <- b) yield ele * ele

// 结合if守卫,仅转换须要的元素

val a3 = for (ele <- if ele % 2 == 0) yield ele *
ele

// 使用函数式编制程序转换数组(经常采取第三种艺术)

a.filter(_ % 2 == 0).map(2 * _)

a.filter { _ % 2 == 0 } map { 2 * _ }

算法案例:移除第三个负数之后的保有负数

// 营造数组

val a = ArrayBuffer[Int]()

a += (1, 2, 3, 4, 5, -1, -3, -5, -9)

// 每发现二个率先个负数之后的负数,就开始展览移除,质量较差,多次平移数组

var foundFirstNegative = false

var arrayLength = a.length

var index = 0

while (index < arrayLength) {

  if (a(index) >= 0) {

    index += 1

  } else {

    if (!foundFirstNegative) { foundFirstNegative =
true; index += 1 }

    else { a.remove(index); arrayLength -= 1 }

  }

}

算法案例:移除第二个负数之后的装有负数(改正版)

// 重新营造数组

val a = ArrayBuffer[Int]()

a += (1, 2, 3, 4, 5, -1, -3, -5, -9)

// 每记录全数不必要移除的元素的目录,稍后三回性移除全部必要移除的因素

// 质量较高,数组内的因素迁移只要实施二回即可

var foundFirstNegative = false

val keepIndexes = for (i <- 0 until a.length if
!foundFirstNegative || a(i) >= 0) yield {

  if (a(i) < 0) foundFirstNegative = true

  i

}

for (i <- 0 until keepIndexes.length) { a(i) =
a(keepIndexes(i)) }

a.trimEnd(a.length – keepIndexes.length)

创建Map

// 创立三个不可变的Map

val ages = Map(“Leo” -> 30, “Jen” -> 25, “Jack”
-> 23)

ages(“Leo”) = 31

// 创设1个可变的Map

val ages = scala.collection.mutable.Map(“Leo” ->
30, “Jen” -> 25, “Jack” -> 23)

ages(“Leo”) = 31

// 使用别的一种办法定义Map成分

val ages = Map((“Leo”, 30), (“Jen”, 25), (“Jack”,
23))

// 创立三个空的HashMap

val ages = new
scala.collection.mutable.HashMap[String, Int]

访问Map的元素

// 获取内定key对应的value,如若key不存在,会报错

val leoAge = ages(“Leo”)

val leoAge = ages(“leo”)

// 使用contains函数检查key是或不是存在

val leoAge = if (ages.contains(“leo”)) ages(“leo”)
else 0

// getOrElse函数

val leoAge = ages.getOrElse(“leo”, 0)

修改Map的元素

// 更新Map的元素

ages(“Leo”) = 31

// 扩展多少个要素

ages += (“Mike” -> 35, “Tom” -> 40)

// 移除元素

ages -= “Mike”

// 更新不可变的map

val ages2 = ages + (“Mike” -> 36, “Tom” ->
40)

// 移除不可变map的因素

val ages3 = ages – “Tom”

遍历Map

// 遍历map的entrySet

for ((key, value) <- ages) println(key + ” ” +
value)

// 遍历map的key

for (key <- ages.keySet) println(key)

// 遍历map的value

for (value <- ages.values) println(value)

// 生成新map,反转key和value

for ((key, value) <- ages) yield (value,
key)

SortedMap和LinkedHashMap

// SortedMap可以自行对Map的key的排序

val ages = scala.collection.immutable.SortedMap(“leo”
-> 30, “alice” -> 15, “jen” -> 25)

// LinkedHashMap能够记住插入entry的逐1

val ages = new
scala.collection.mutable.LinkedHashMap[String, Int]

ages(“leo”) = 30

ages(“alice”) = 15

ages(“jen”) = 25

Map的要素类型—Tuple

// 简单Tuple

val t = (“leo”, 30)

// 访问Tuple

t._1

// zip操作

val names = Array(“leo”, “jack”, “mike”)

val ages = Array(30, 24, 26)

val nameAges = names.zip(ages)

for ((name, age) <- nameAges) println(name + “: “

  • age)

概念贰个简便的类

// 定义类,包括田野以及艺术

class HelloWorld {

  private var name = “leo”

  def sayHello() { print(“Hello, ” + name) } 

  def getName = name

}

// 制造类的目的,并调用其艺术

val helloWorld = new HelloWorld

helloWorld.sayHello()

print(helloWorld.getName) // 也得以不加括号,假如定义方法时不带括号,则调用方法时也不能够带括号

getter与setter

壹、定义不带private的
var 田野同志,此时scala生成class时,会自动生成1个private[this]的积极分子字段(名称与田野先生不相同),并还生成1对getter和setter方法,分外号称为田野(field)和 田野同志_=,并且getter和setter方法的造访修饰符与田野定义相同

2、而假设运用private修饰田野同志,则只变动的getter和setter,且访问修饰也是private的

三、即使定义val
田野先生,则只会生成getter方法

4、
借使不期望生成setter和getter方法,则将田野先生表明为private[this]

class Student {

  var name = “leo”

}

// 调用getter和setter方法,分小名称为name和name_=

val leo = new Student

print(leo.name)

leo.name = “leo一” //实际上会调用 leo.name_=(“leo1”)方法

自定义getter与setter

// 假如只是希望拥有简单的getter和setter方法,那么就依据scala提供的语法规则,依据必要为田野选拔适当的修饰符就好:var、val、private、private[this]

// 不过1旦期望能够团结对getter与setter实行控制,则足以自定义getter与setter方法

// 自定义setter方法的时候肯定要注意scala的语法限制,签名、=、参数间不可能有空格

class Student {

  private var myName = “leo” //暗中同意会生成一对private
getter(myName)、setter(myName _=)方法

  def name = “your name is ” + myName //自定义myName
成员变量getter方法

  def name_=(newValue: String)  {//自定义myName
成员变量的setter方法

    print(“you cannot edit your name!!!”)

  }

}

val leo = new Student

print(leo.name)

leo.name = “leo1” //会去调用 name_+
自定义setter 方法

仅暴露field的getter方法

// 假诺你不愿意田野有setter方法,则足以定义为val,可是此时就再也不能够更改田野先生的值了

// 但是一旦希望能够一味暴表露二个getter方法,并且仍是能够因而1些方法更改田野(field)的值,那么须求综合应用private以及自定义getter方法。此时,由于田野是private的,所以setter和getter都以private,对外场未有揭示;自身能够兑现修改田野值的点子;自身能够覆盖getter方法

class Student {

  private var myName = “leo”

  def updateName(newName: String) { //更改田野的其它方法(命名约束的不满意setter方法)

    if(newName == “leo1”) myName = newName

    else print(“not accept this new name!!!”)

  }

  def name = “your name is” + myName  //覆盖自动生成的私人住房getter方法

}

private[this]的使用

// 假使将田野同志使用private来修饰,那么代表这么些田野同志是类民用的,在类的主意中,能够直接待上访问类的其余对象的private 田野(field)

// 那种景色下,假使不指望田野(field)被别的对象访问到,那么能够应用private[this],意味着对象个人的田野同志,唯有本对象内足以访问到(子类对象中也是不得以访问的,因为个人的是无法被接续的)

class Student {

  private var myAge = 0 //试着修改成private[this]

  def age_=(newValue: Int) {

    if (newValue > 0) myAge = newValue

    else print(“illegal age!”)

  }

  def age = myAge

  def older(s: Student) = {

    myAge > s.myAge //修改成private[this]后,就会报错

  }

}

private[this]还足以用为修章

Java风格的getter和setter方法

// Scala的getter和setter方法的命名与java是见仁见智的,是田野(field)和田野(field)_=的方式

// 如若要让scala自动生成java风格的getter和setter方法,只要给田野(field)添加@BeanProperty注明即可

// 此时会变动多少个主意,name: String、name_=(newValue:
String): Unit、getName(): String、setName(newValue:
String): Unit

import scala.reflect.BeanProperty

class Student {

  @BeanProperty var name: String = _

}

class Student(@BeanProperty var name: String)

val s = new Student

s.setName(“leo”)

s.getName()

辅助constructor

// Scala中,能够给类定义四个帮忙constructor,类似于java中的构造函数重载

// 协助constructor之间能够并行调用,而且必须首先行调用主constructor或任何辅构造器

class Student {

  private var name = “”

  private var age = 0

  def this(name: String) {

    this()

    this.name = name

  }

  def this(name: String, age: Int) {

    this(name)

    this.age = age

  }

}

主constructor

// Scala中,主constructor是与类名放在1起的,有且唯有三个,与java分裂

// 而且类中,没有定义在其余方法中的代码(包蕴成员字段),都属于主constructor的代码,且执行的依次与代码书写的依次一致。那实则与Java是千篇一律的,在Java中艺术之外的代码(成员以及代码块)会在构造器调用从前起先执行,姑且将这一个代码看作也是放置了贰个主构造器中举办实施的,只可是那种主构造器不能够带构造参数

//主构造器与类定义是在协同的,固然有参数,则在类名后边跟括号即可:

class Student(val name: String, val age: Int)
{

  println(“your name is ” + name + “, your age is ” +
age)

}

当然未有参数的主构造器也足以带括号:

class Student() {}

 

// 主constructor中还足以因而选取暗中同意参数,来给参数暗许的值

class Student(val name: String = “leo”, val age: Int
= 30) {

  println(“your name is ” + name + “, your age is ” +
age)

}

// 假如主constrcutor传入的参数什么修饰都不曾,比如name: String,那么一旦类内部除主constrcutor方法外别的格局也采用到了,则会自动将该参数修饰为private[this] name以便其它方法运用:

class Student(name: String) {

         def f(){print(name)}

         def
f(s:Student){print(s.name)}//编写翻译出错

}

class Student(val name: String) {

         def f(){print(name)}

         def
f(s:Student){print(s.name)}//编写翻译正确,申明没有使用var或val修饰时,且在除主构造器中应用外,则接纳private[this]来修饰

}

类中从不概念的在任何方法中的代码都属于主构造器,并且实施顺序与书写顺序1致:

威尼斯人6799.com 4

内部类

// Scala中,同样能够在类中定义内部类;不过与java差别的是,每一个外部类的对象的个中类,都属于分裂的类

import scala.collection.mutable.ArrayBuffer

class Class {

  class Student(val name: String) {}

  val students = new ArrayBuffer[Student]

  def getStudent(name: String) =  {

    new Student(name)

  }

}

val c1 = new Class

val s1 = c1.getStudent(“leo”)

c1.students += s1

val c2 = new Class

val s2 = c2.getStudent(“leo”)

c1.students += s2   //异常

object

一、object,也正是class的单个实例(但与从class实例化出来的目的的情节并非1样),日常在其间放一些class层面上共享的始末,如Java中的静态田野(field)可能method即在概念在object中(注:Scala中并未有Java的静态概念,所以延伸出了object那么些东东)

二、你能够将object看作是1个类class,只是那些类在内部存款和储蓄器中唯有二个单例,且定义的object名正是实例名,不需大家温馨实例化,运转时JVM已帮大家new出来了

三、第一回调用object的点辰时,就会执行object的constructor,相当于object内部不在method中的代码;不过object无法定义接受参数的constructor

4、注意,object的constructor只会在其首先次被调用时实施1遍,将来再也调用就不会重新实施constructor了

五、object平日用于作为单例情势的完结,恐怕放class的静态成员,比如工具方法

object Person {

  private var eyeNum = 2

  println(“this Person object!”)

  def getEyeNum = eyeNum

}

伴生对象

// 若是有3个class,还有三个与class同名的object,那么就称那几个object是class的伴生对象,class是object的伴生类

// 伴生类和伴生对象必须存放在三个.scala文件之中

// 伴生类和伴生对象,最大的表征就在于,互相能够访问private 田野

object Person {

  private val eyeNum = 2

  def getEyeNum = eyeNum

}

class Person(val name: String, val age: Int) {

  def sayHello = println(“Hi, ” + name + “, I guess
you are ” + age + ” years old!” + “, and usually you must have ” +
Person.eyeNum + ” eyes.”)

}

让object继承抽象类

// object的功能实在和class类似,除了不能够定义接受参数的constructor之外

// object也能够持续抽象类,并掩盖抽象类中的方法

abstract class Hello(var message: String) {

  def sayHello(name: String): Unit

}

object HelloImpl extends Hello(“hello”) {

  override def sayHello(name: String) = {

    println(message + “, ” + name)

  }

}

apply方法

// object中尤其主要的1个新鲜情势,就是apply方法

// 日常在伴生对象中贯彻apply方法,并在在那之中落实协会伴生类的靶子的效用,壹般作为工厂方法

// 而创建伴生类的指标时,常常不会接纳new Class的主意,而是选拔Class()的秘诀,隐式地调用伴生对象得apply方法,那样会让对象创制特别简明

// 比如,Array类的伴生对象的apply方法就兑现了收到可变多少的参数,并创制1个Array对象的法力

val a = Array(1, 2, 3, 4, 5)

// 比如,定义本人的伴生类和伴生对象

class Person(val name: String)

object Person {

  def apply(name: String) = new Person(name)

}

 

除此以外,如若直白在3个对象前面接小括号,则会去调用那个指标所对应类中相应的apply方法:

class Person {

  def apply(name: String) = println(name)

}

scala> val p = new Person

scala> p(“Persion”)

Persion

main方法

// 就就如java中,就算要运转多个先后,必须编写制定二个蕴含main方法类一样;在scala中,要是要运维2个应用程序,那么必须有2个main方法,作为入口

// scala中的main方法定义为def
main(args: Array[String]),而且必须定义在object中

object HelloWorld {

  def main(args: Array[String]) {

    println(“Hello World!!!”)

  }

}

// 除了自个儿达成main方法之外,还足以继承App Trait,然后将供给在main方法中运维的代码,直接作为object的constructor代码;而且用args还行传入的参数

object HelloWorld extends App {

  if (args.length > 0) println(“hello, ” +
args(0))

  else println(“Hello World!!!”)

}

 

// 假使要运转上述代码,必要将其放入.scala文件,然后先使用scalac编写翻译,再用scala执行

scalac HelloWorld.scala

scala -Dscala.time HelloWorld

// App Trait的劳作规律为:App Trait继承自DelayedInit Trait,scalac命令实行编译时,会把继承App Trait的object的constructor代码都放到DelayedInit
Trait的delayedInit方法中,然后由App
Trait的main方法去调用执行

用object来促成枚举成效

// Scala未有一贯提供类似于Java中的Enum那样的枚举本性,假若要促成枚举,则须求用object继承Enumeration类,并且调用Value方法来开首化枚举值

object Season extends Enumeration {

  val SPRING, SUMMER, AUTUMN, WINTER = Value

}

// 还可以够透过Value传入枚举值的id和name,通过id和toString能够拿走;
还能通过id和name来寻找枚举值

object Season extends Enumeration {

  val SPRING = Value(0, “spring”)

  val SUMMER = Value(1, “summer”)

  val AUTUMN = Value(2, “autumn”)

  val WINTER = Value(3, “winter”)

}

Season(0)  // spring

Season.withName(“spring”) // spring,遵照名称找

// 使用枚举object.values能够遍历枚举值

for (ele <- Season.values) println(ele)

extends

// Scala中,让子类继承父类,与Java一样,也是使用extends关键字

// 继承就表示,子类能够从父类继承父类的田野和method;然后子类能够在友好之中放入父类所未曾,子类特有的田野和method;使用持续能够使得复用代码

// 子类能够覆盖父类的田野(field)和method;但要注意的是final类是无法被持续的,而且final类型的田野同志和method是无法被遮盖的

class Person {

  private var name = “leo”

  def getName = name

}

class Student extends Person {

  private var score = “A”

  def getScore = score

}

override和super

// Scala中,要是子类要遮盖2个父类中的非抽象方法,则必须使用override关键字;假设是空虚的方法,则能够省略

// override关键字可以帮忙大家尽快地意识代码里的荒唐,比如:override修饰的父类方法的章程名大家拼写错了;比如要覆盖的父类方法的参数大家写错了;等等

// 别的,在子类覆盖父类方法之后,假诺大家在子类中正是要调用父类的被遮住的办法吧?那就足以使用super关键字,显式地钦赐要调用父类的主意

class Person {

  private var name = “leo”

  def getName = name

}

class Student extends Person {

  private var score = “A”

  def getScore = score

  override def getName = “Hi, I’m ” + super.getName

}

重写时索要override关键字,假如是促成则能够省略override关键字

override field

子类能够覆盖父类的同名的非private成员

// Scala中,子类能够覆盖父类的val
田野,而且子类的val 田野同志仍可以覆盖父类的val 田野(field)的getter方法;只要在子类中选用override关键字即可

class Person {

  /*private*/ val name: String = “Person”

}

class Student extends Person {

  override val name: String = “leo” // 重写一定要带上override关键字

}

 

唯有val变量才能被重写,var变量是不可能被重写的:

class A{

         var f = “a”

}

class B extends A{

         override var f = “b” // error: overriding
variable f in class A of type String;

                                            
//variable f cannot override a mutable variable

}

上边也是格外的:

class A{

         var f:String = “a”

}

class B extends A{

         override def f:String = “b”

         override def f_=(x:String) = println(x) //
error: overriding variable f in class A of type String;

                                                                        
   //method f_= cannot override a mutable variable

}

 

var变量只好被完结,假设将方面换来肤浅的var字段,则是能够的:

abstract class A{

         var f:String

}

class B extends A{

         /* override */ def f:String = “b”
//由于是贯彻,所以能够省略override

         override def f_=(x:String) = println(x)
//也可以不省略override

}

或者:

abstract class A{

         var f:String

}

class B extends A{

         var f:String = “”

}

 

val变量只可以被val完结,不能被def落成:

abstract class A{

         val f:String

}

class B extends A{

         def f:String = “”  // error: overriding
value f in class A of type String;

                                             //method
f needs to be a stable, immutable value

}

但足以如此:

abstract class A{

         val f:String

}

class B extends A{

         val f:String = “”

}

val、var override/实现 def

abstract
class Person {

    def id:
Int  

}

class
Student extends Person{

   
override var id = 9527  //Error: method id_= overrides nothing

}

在scala中定义了三个var变量,会自动生成getter和setter方法。由于父类中只定义了三个措施def id: Int,而子类中var变量会自动生成getter(id)与setter方法(id_),不过父类并从未这一个setter方法,所以是无力回天重写的。如下修改即可:

abstract class Person {

    def id: Int  

    def id_=(value: Int) //父类必须有set方法

}

class Student extends Person{

    override var id = 9527 //为var变量自动生成get和set方法

}

要么子类定义成val变量:

abstract class Person {

    def id: Int  

}

class Student extends Person{

    override val id = 9527

}

 

上面是val或var来实现def,下面是val或var来重写def:

class Person {

    def id: Int = 1

}

class Student extends Person{

    override val id = 9527 

}

或

class Person {

    def id: Int = 1

    def id_=(value: Int) =println(value) 

}

class Student extends Person{

    override var id = 9527 

}

 

但是不能使用def重写val或var:

class Person {

  val sex: String 

}

class Student extends Person {

  override def sex:String = "" // error: overriding value sex in class Person of type String;

                                      //method sex needs to be a stable, immutable value

}

 

class Person {

  var sex: String = "X"

}

class Student extends Person {

  override def sex:String = "" 

  override def sex_=(x:String) = println(x)

}

也不能使用def实现val:

abstract class Person {

  val sex: String 

}

class Student extends Person {

  def sex:String = "" // error: overriding value sex in class Person of type String;

                               //method sex needs to be a stable, immutable value

}

但可以使用def实现var:

abstract class Person {

  var sex: String 

}

class Student extends Person {

  def sex:String = ""

  def sex_=(x:String) = println(x)

}

 

成员变量与方法之间重写与实现结论:可以使用val或var来重写或实现def,也可以使用def实现var;但不能使用def重写val或var,也不能使用def实现val

isInstanceOf和asInstanceOf

// 假设大家创制了子类的对象,然而又将其给予了父类类型的变量。则在延续的先后中,大家又须求将父类类型的变量转换为子类类型的变量,应该如何是好?

// 首先,须求运用isInstanceOf判断目的是或不是是内定类的目的,尽管是的话,则足以采取asInstanceOf将指标转换为内定项目

// 注意,如若目的是null,则isInstanceOf一定重临false,asInstanceOf一定重临null

// 注意,假诺未有用isInstanceOf先判断目的是还是不是为钦命类的实例,就一向用asInstanceOf转换,则恐怕会抛出相当

class Person

class Student extends Person

val p: Person =  new Student

var s: Student = null

if (p.isInstanceOf[Student]) s =
p.asInstanceOf[Student]

 

scala> p.isInstanceOf[Student]

res7: Boolean = true

scala> p.isInstanceOf[Person]

res8: Boolean = true

getClass和classOf

// isInstanceOf只可以判断出指标是还是不是是给定类或其子类的实例对象,而不能够精确判断出目的正是给定类的实例对象

// 假若需求标准地判定目的就是钦赐类的靶子,那么就不得不选拔getClass和classOf了

// 对象.getClass可以准确获取对象所属的类class,classOf[类]能够规范获取类,然后选拔==操作符即可判断

class Person

class Student extends Person

val p: Person = new Student

p.isInstanceOf[Person]

p.getClass == classOf[Person]

p.getClass == classOf[Student]

行使方式相配举办项目判断

// 不过在事实上花费中,比如spark的源码中,多量的地方都是运用了形式相配的章程来进展项指标判定,那种办法进一步太子参简,而且代码得可维护性和可扩张性也丰盛的高

// 使用方式相称,功效性上来说,与isInstanceOf一样,也是判定主若是此类以及该类的子类的靶子即可,也不是精准判断的

class Person

class Student extends Person

val p: Person = new Student

p match {

  case per: Person => println(“it’s Person’s
object”)

  case _  => println(“unknown type”)

}

protected

// 跟java1样,scala中壹样能够动用protected关键字来修饰田野和method,这样子类就足以继承这一个分子或方法

// 还能利用protected[this],则只可以在脚下子类对象中访问父类的运用protected[this]修饰的田野(field)和method,不能透过别的子类对象访问父类中的那么些字段与措施

class Person {

  protected var name: String = “leo”

  protected[this] var hobby: String = “game”

}

class Student extends Person {

  def sayHello = println(“Hello, ” + name)

  def makeFriends(s: Student) {

    println(“my hobby is ” + hobby + “, your hobby is
” + s.hobby) //此处编写翻译出错

  }

}

protected[this]修饰的字段只还好本对象或其子对象中采用,不能够在任何对象中选取:

class Person {

  protected var name: String = “leo”

  protected[this] var hobby: String = “game”

  def makeFriends(s: Person ){

    println(“my hobby is ” + hobby + “, your hobby is
” + s.hobby) //此处编写翻译依然出错

  }

}

与private[this]一样,protected[this]也得以修饰方法

调用父类的constructor

// Scala中,各样类能够有叁个主constructor和肆意多少个帮扶constructor,而各种协助constructor的第一行都必须是调用别的接济constructor或然是主constructor;因而子类的相助constructor是一定不也许一贯调用父类的constructor的

// 只可以在子类的主constructor中调用父类的constructor,以下那种语法,就是经过子类的主构造函数来调用父类的构造函数(即在extends前面钦定要求调用父类哪个构造器)

// 注意!要是是父类中收取的参数,比如name和age,子类中收受时,就无须用此外val或var来修饰了(也许带上修饰了,但将参数名命成不等同也可),不然会以为是子类要遮盖父类的田野同志

class Person(val name: String, val age: Int)

class Student(name: String, age: Int, var score:
Double) extends Person(name, age) /*调用父类的帮带构造器*/{

  def this(name: String) {

    this(name, 0, 0) //调用主构造器

  }

  def this(age: Int) {

    this(“leo”, age, 0) //调用主构造器

  }

}

调用父类的主构造器:

class Person(val name: String, val age: Int){

         def this(){

                   this(“11”,11)

         }

}

class Student(name: String, age: Int, var score:
Double) extends Person/*或 Person()*/
{

  def this(name: String) {

    this(name, 0, 0) //调用主构造器

  }

  def this(age: Int) {

    this(“leo”, age, 0) //调用主构造器

  }

}

匿名内部类

// 在Scala中,匿名子类是那么些常见,而且十三分有力的。斯Parker的源码中也大量选拔了那种匿名子类。

// 匿名子类,也正是说,能够定义多个类的从未有过称谓的子类,并一贯开立其指标,然后将对象的引用赋予1个变量。之后甚至足以将该匿名子类的目的传递给其余函数使用。

class Person(protected val name: String) {

  def sayHello = “Hello, I’m ” + name

}

val p = new Person(“leo”) {

  override def sayHello = “Hi, I’m ” + name

}

def greeting(p: Person { def sayHello: String })
{

  println(p.sayHello)

}

抽象类

// 要是在父类中,有壹些方法不恐怕即时落到实处,而急需借助差异的子来来掩盖,重写达成自身分歧的格局达成。此时得以将父类中的那些方法不交付具体的落成,唯有方法签名,那种方法正是空虚方法。

// 而三个类中只要有贰个华而不实方法,那么类就必须用abstract来声称为抽象类,此时抽象类是不得以实例化的

// 在子类中覆盖抽象类的虚幻方法时,不要求运用override关键字(也可带上),但即便是重写父类具体方法或成员,则无法省略override

abstract只好修饰类,不能够修饰成员与办法,哪怕成员(未有初步化)与办法(未有方法体)是架空的

 

abstract class Person(val name: String) {

  def sayHello: Unit

}

class Student(name: String) extends Person(name)
{

  def sayHello: Unit = println(“Hello, ” +
name)

}

抽象field

// 若是在父类中,定义了田野(field),不过从未给出初始值,则此田野先生为抽象田野先生

// 抽象田野同志意味着,scala会依据自个儿的条条框框,为var或val类型的田野(field)生成对应的getter和setter方法,不过父类中是不曾该田野的

// 子类必须覆盖田野,以定义本人的具体田野同志,并且覆盖抽象田野先生,不要求选拔override关键字

abstract class Person {

  val name: String

}

class Student extends Person {

  val name: String = “leo”

}

 

 

未有开首化的分子所在的类借使抽象类:

abstract class A{

         var a:String

}

 

/*class B extends A*/编写翻译时报错:须求重写父类的悬空成员

class B extends A{

         /*override*/ var a:String = “a”
//也得以省略override

}

除外通过上面间接覆盖父类的空洞成员外,还足以简接通过落实抽象成员所对应的getter与setter方法即可:

class B extends A{

         /*override*/ def a = “a” //由于是兑现,所以可以省略override

         override def
a_=(x:String){println(a)}

}

地点是因而兑现父类抽象成员所对应的getter与setter方法来重写抽象成员,所以可以看来:没有被开首化的成员所对应的getter与setter方法实质上正是抽象的,所以类要定义是abstract,成员字段自个儿并未有怎么抽象不空洞的定义

将trait作为接口使用

// Scala中的Triat是一种奇特的概念

// 首先大家能够将Trait作为接口来使用,此时的Triat就与Java中的接口卓殊周围

// 在triat中能够定义抽象方法,就与抽象类中的抽象方法一致,只要不交付方法的切实可行落到实处即可

// 类能够使用extends关键字继承trait,注意,那里不是implement,而是extends,在scala中向来不implement的概念,无论继承类照旧trait,统一都是extends

// 类继承trait后,必须实现个中的肤浅方法(倘使是trait继承trait则不须要,那好比Java中的接口继承接口壹样),达成时不需求选用override关键字

// scala不帮助对类举办多一而再,然而协助多重继承trait,使用with关键字即可

trait HelloTrait {

  def sayHello(name: String)

}

trait MakeFriendsTrait {

  def makeFriends(p: Person)

}

class Person(val name: String) extends HelloTrait
with MakeFriendsTrait with Cloneable {

  def sayHello(name: String) = println(“Hello, ” +
name)

  def makeFriends(p: Person) =
{sayHello(name);println(“Hello, my name is ” + name + “, your name is “

  • p.name)}

}

val p1 = new Person(“leo”)

val p2 = new Person(“lily”)

p1.makeFriends(p2)

在Trait中定义具体方法

// Scala中的Triat能够不是只定义抽象方法,还可以定义具体方法,此时trait更像是包蕴了通用工具方法的事物
// 有三个专有的名词来形容那种景况,正是说trait的功用混入了类

// 举例来说,trait中得以包含部分浩大类都通用的功能方法,比如打字与印刷日志等等,spark中就选择了trait来定义了通用的日志打字与印刷方式

trait Logger {

  def log(message: String) = println(message)

}

class Person(val name: String) extends Logger
{

  def makeFriends(p: Person) {

    println(“Hi, I’m ” + name + “, I’m glad to make
friends with you, ” + p.name)

    log(“makeFriends methdo is invoked with parameter
Person[name=” + p.name + “]”)

  }

}

val p1 = new Person(“leo”)

val p2 = new Person(“lily”)

p1.makeFriends(p2)

在Trait中定义具体字段

// Scala中的Triat能够定义具体田野先生,此时此起彼伏trait的类就活动获得了trait中定义的田野

trait Person {

  val eyeNum: Int = 2

}

class Student(val name: String) extends Person
{

  def sayHello = println(“Hi, I’m ” + name + “, I
have ” + eyeNum + ” eyes.”)

}

val s = new Student(“leo”)

s.sayHello

在Trait中定义抽象字段

// Scala中的Triat能够定义抽象田野(field),而trait中的具体方法则足以依据抽象田野(field)来编排

// 然而后续trait的类,则必须覆盖抽象田野先生,提供具体的值

trait SayHello {

  val msg: String //抽象字段

  def sayHello(name: String) = println(msg + “, ” +
name) // 具体方法调用抽象字段(实质上是调用val抽象字段所对应的getter抽象方法),约等于Java中的方式方法,另参看那里

}

class Person(val name: String) extends SayHello
{

  val msg: String = “hello”

  def makeFriends(p: Person) {

    sayHello(p.name)

    println(“I’m ” + name + “, I want to make friends
with you!”)

  }

}

val p1 = new Person(“leo”)

val p2 = new Person(“lily”)

p1.makeFriends(p2)

为实例混入trait

// 有时大家能够在成立类的对象时,钦赐该目的混入某些trait,那样,就只有这些指标混入该trait的主意,而类的别的对象则从未

trait Logged {

  def log(msg: String) {}

}

trait MyLogger extends Logged {

  override def log(msg: String) { println(“log: ” +
msg) }

class Person(val name: String) extends Logged
{

    def sayHello { println(“Hi, I’m ” + name);
log(“sayHello is invoked!”) }

}

val p1 = new Person(“leo”)

p1.sayHello  // Hi, I’m leo

val p2 = new Person(“jack”) with MyLogger //实例化时混入

p2.sayHello  // Hi, I’m jack

//log: sayHello is invoked!

trait调用链

// Scala中协理让类继承多个trait后,依次调用多少个trait中的同叁个艺术,只要让多少个trait的同2个格局中,在方式最终都举行“super.方法”来调用父类方法即可

// 类中调用多少个trait中都一些那些格局时,首先会从最右侧的trait的艺术初始实施,然后依次往左执行,形成3个调用链条

// 那种特征13分强劲,其实就也正是设计情势中的权利链方式的一种具体落到实处

trait Handler {

  def handle(data: String) {}

}

trait DataValidHandler extends Handler {

  override def handle(data: String) {

    println(“check data: ” + data)

    super.handle(data)

  }

}

trait SignatureValidHandler extends Handler {

  override def handle(data: String) {

    println(“check signature: ” + data)

    super.handle(data)

  }

}

class Person(val name: String) extends
SignatureValidHandler with DataValidHandler {

  def sayHello = { println(“Hello, ” + name);
handle(name) }

}

val p = new Person(“leo”)

p.sayHello

 

Hello, leo

check data: leo

check signature: leo

在trait中覆盖抽象方法

// 在trait中,是足以覆盖父trait的悬空方法的

// 不过覆盖时,假若利用了“super.方法”格局调用了父类抽象方法,则无从通过编写翻译。因为super.方法就会去掉用父trait的悬空方法,此时子trait的该办法还是会被认为是架空的,所以在override的同时还索要加上abstract

// 此时一经要透过编写翻译,就得给子trait的格局加上abstract
override修饰

trait Logger {

  def log(msg: String)

}

trait MyLogger extends Logger {

  abstract override
def log(msg: String) { println(“MyLogger.log()”);super.log(msg) }

}

class BasicLog extends Logger{

def log(msg: String) { println(“BasicLog.log()”);
println(msg) }

}

class Person(val name: String) extends BasicLog with
MyLogger {

  def makeFriends(p: Person) {

    println(“Hi, I’m ” + name + “, I’m glad to make
friends with you, ” + p.name)

    log(“makeFriends methdo is invoked with parameter
Person[name=” + p.name + “]”)

  }

}

val p1 = new Person(“leo”)

val p2 = new Person(“lily”)

p1.makeFriends(p2)

 

Hi, I’m leo, I’m glad to make friends with you,
lily

MyLogger.log()

BasicLog.log()

makeFriends methdo is invoked with parameter
Person[name=lily]

混合使用trait的具体方法和浮泛方法

// 在trait中,能够勾兑使用具体方法和架空方法

// 能够让具体方法依赖于肤浅方法,而肤浅方法则停放继承trait的类中去落到实处

// 那种trait其实就是设计情势中的模板设计格局的反映

trait Valid { 

  def getName: String  //抽象方法

  def valid: Boolean = { //具体方法中调用抽象方法,也正是Java中的模板方法

    getName == “leo”   

  }

}

class Person(val name: String) extends Valid {

  println(valid)

  def getName = name

}

val p = new Person(“leo”) //true

trait的布局机制

// 在Scala中,trait也是有组织代码的,约等于trait中的,不含有在别的格局中的代码

// 而一而再了trait的类的结构机制如下:一、父类的构造函数执行;2、trait的构造代码执行,三个trait从左到右依次执行;叁、构造trait时会先构造父trait,若是三个trait继承同八个父trait,则父trait只会协会三次;4、全部trait构造实现之后,子类的构造函数执行

class Person { println(“Person’s constructor!”)
}

trait Logger { println(“Logger’s constructor!”)
}

trait MyLogger extends Logger { println(“MyLogger’s
constructor!”) }

trait TimeLogger extends Logger {
println(“TimeLogger’s constructor!”) }

class Student extends Person with MyLogger with
TimeLogger {

  println(“Student’s constructor!”)

}

val s = new Student

trait 田野的先河化

// 在Scala中,trait的构造函数是不可能接参数的(包含主构造器与救助构造器),即trait不可能定义帮忙构造器,那是trait与class的绝无仅有分歧,然而假设供给正是要trait能够对田野同志进行起首化,该如何做吧?只可以动用Scala中非凡特殊的一种高级特性——提前定义

trait SayHello {

  val msg: String

  println(“1、SayHello”)

  println(msg.toString) // 抛NullPointerException分外。由于在调用msg成员字段时,发以往msg是被重新达成(或重写,那里为贯彻),则会去调用子类中的完成的msg成员,但由于此时子类构造器还未实行,所以子类msg还没赶趟伊始化,所以回来null,最后造成空指针相当

}

class Person extends SayHello{

 println(“2、Person”)

 val msg:String = “init”

}

new Person // 抛NullPointerException非凡,原因父trait构造代码会先于子类构造器执行,在履行msg.toString时子类中的msg还一向不来得及伊始化。但万壹将下边的val都修改为def,则能够正常运作。因为初叶化父类时,由于子类完毕(或重写,那里为兑现)了msg方法,所以msg.toString会去调用子类完结的msg方法而回到”init”,就算此时子类还尚未被早先化:

trait SayHello {

  def msg: String

  println(“1、SayHello”)

  println(msg.toString)

}

class Person extends SayHello{

 println(“2、Person”)

 def msg:String = “init”

}

new Person

 

便是父类提供了初阶化,但要么抛NullPointerException,原因是子类重写了父类该字段msg,在履行父类构造器中的msg.toString时,msg使用的是子类中被重写过的,但此时子类构造器还未被实施,所以子类的msg此时还为null:

trait SayHello {

  val msg: String = “000”

  println(“1、SayHello”)

  println(msg.toString) //
NullPointerException

}

class Person extends SayHello{

 println(“2、Person”)

 override val msg:String = “init”

}

new Person

而下边包车型地铁则不会抛格外了,原因是子类未有重写msg字段,所以父类构造器在实施时,msg使用的照旧父中的msg,且早已被开始化过了:

trait SayHello {

  val msg: String = “000”

  println(“1、SayHello”)

  println(msg.toString) // 不会抛非凡,注意:此名要放在上边msg初叶化语句的背后,否则还是会抛空指针

}

class Person extends SayHello{

 println(“2、Person”)

}

new Person

 

上面依照前边的学识(字段与措施互相落成与重写),结合方面包车型地铁阅历,分析分析一下底下的状态:

以下也足以,原因也是透过措施的多态来开头化:

trait SayHello {

  var msg: String

  println(“1、SayHello”)

  println(msg.toString)//会去调用子类实现情势msg,顺利执行

}

class Person extends SayHello{

 println(“2、Person”)

 def msg:String = {println(“person.msg”);”init”
}

 def msg_=(x:String) = println(x)

}

new Person

 

trait SayHello {

  var msg:
String

  println(“1、SayHello”)

  println(msg.toString) // 抛 NullPointerException,原因父类中的msg被子类完毕过,但父类调用时,子类还未起先msg字段

}

class Person extends SayHello{

 println(“2、Person”)

 var msg:String =
“init”

}

new Person

下边除了通过调用子类完成(或重写)方法化解难点外,下边还能透过提前定义格局来伊始化:

trait SayHello {

  val msg: String

  println(“3、SayHello”)

  println(msg.toString)

}

class Person{println(“2、Person”)}

val p = new {

  val msg: String = {println(“一、init”);”init”}  //
实例化时提前开端化

} with Person with SayHello

1、init -> 2、Person ->
3、SayHello

只顾上边new …
with与class …extends…with的区分,new…with是动态混入,执行构造器是从new前面包车型客车类(或块,那里为块)起始从左到右依次执行;而class…extends…with则是静态混入,在定义class时就已规定,其构造器是从extends前面包车型大巴类伊始从左往右依次执行,执行完后最终执行class 后边钦赐的类的构造器。如上面包车型地铁new … with方式组织顺序:

trait A{

 println(“a”)

}

class B extends A{

 println(“b”)

}

trait C extends A{

 println(“c”)

}

new B with C // a -> b -> c

 

class…extends…with构造顺序:

trait A{

 println(“a”)

}

trait B{

 println(“b”)

}

class C extends A with B{

 println(“c”)

}

new C // a -> b -> c

 

上面是另一种开头化格局(class …extends…with静态定义格局),此种情势比上面初步化格局好掌握一些:

trait SayHello {

  val msg: String

  println(“2、SayHello”)

  println(msg.toString)

}

class Person extends {

  val msg: String = {println(“一、init”);”init”} //
类定义时提前开端化

} with SayHello {

  println(“3、Person”)

}

new Person

 

// 其它一种形式就是使用lazy value

trait SayHello {

  lazy val msg: String = {println(“SayHello”);null}
// 此句不会实施

  println(msg.toString) // 此句会调用子类重写过的msg成员,由于子类msg定义成了lazy,而lazy变量有脾气状就是在动用时会执行左边表明式,所以在那边调用msg.toString方法时,就会触发懒加载左边的测算表达式,所以lazy字段不是由类来开端化的,而是由调用时机来支配,所以子类中的lazy msg会先于子类别的成员被早先化

  println(“2”)

}

class Person extends SayHello {

  println(“3”)

  val m: String = {println(“4″);”m”}

  override lazy val msg: String =
{println(“1″);”init”}

}

new Person

 

1

init

2

3

4

trait继承class

// 在Scala中,trait也足以继承自class,此时那么些class就会成为富有继续该trait的类的父类

class MyUtil {

  def printMessage(msg: String) = println(msg)

}

trait Logger extends MyUtil {

  def log(msg: String) = printMessage(“log: ” +
msg)

}

class Person(val name: String) extends Logger
{

  def sayHello {

    log(“Hi, I’m ” + name)

    printMessage(“Hi, I’m ” + name)

  }

}

new Person(“leo”).sayHello

 

log: Hi, I’m leo

Hi, I’m leo

将函数赋值给变量

// Scala中的函数是一等公民,能够独自定义,独立存在,而且能够直接将函数作为值赋值给变量

// Scala的语法规定,将函数赋值给变量时,必须在函数前面加上空格和下划线

def sayHello(name: String) { println(“Hello, ” +
name) }

val sayHelloFunc = sayHello _

sayHelloFunc(“leo”)

匿名函数

// Scala中,函数也能够不供给命名,此时函数被称为匿名函数。

// 能够一贯定义函数之后,将函数赋值给有个别变量;也能够将直接定义的匿名函数字传送入其余函数之中

// Scala定义匿名函数的语法规则正是,(参数名: 参数类型) =>
函数体

// 那种匿名函数的语法必须深切明白和明白,在spark的中有大气那样的语法,假使没有控制,是看不懂spark源码的

val sayHelloFunc = (name: String) =>
println(“Hello, ” + name)

sayHelloFunc(“leo”)

 

变量带回来类型:

val sayHelloFunc:String=>Unit = (name: String)
=> println(“Hello, ” + name)

高阶函数

// Scala中,由于函数是一等百姓,由此能够直接将有个别函数字传送入其余函数,作为参数。那几个效果是极端强大的,也是Java那种面向对象的编程语言钻探所不拥有的。

// 接收其余函数作为参数的函数,也被称作高阶函数(higher-order function)

val sayHelloFunc = (name: String) =>
println(“Hello, ” + name)

def greeting(func: (String) => Unit, name: String)
{ func(name) }

greeting(sayHelloFunc, “leo”)

 

Array(1, 2, 3, 4, 5).map((num: Int) => num *
num)

 

// 高阶函数的此外一个效益是将函数作为重返值,即重返值正是1个函数,如下边依据差异的msg生成不相同的函数

def getGreetingFunc(msg: String) = (name: String)
=> println(msg + “, ” + name)

var greetingFunc = getGreetingFunc(“hello”)

greetingFunc(“leo”)

greetingFunc = getGreetingFunc(“hi”)

greetingFunc(“leo”)

高阶函数的种类猜想

// 高阶函数能够活动测算出参数类型,而不需求写明类型;而且对于唯有二个参数的函数,还可以省去其小括号;

def greeting(func: (String) => Unit, name: String)
{ func(name) }

greeting((name: String) => println(“Hello, ” +
name), “leo”)

greeting((name) => println(“Hello, ” + name),
“leo”)

greeting(name => println(“Hello, ” + name),
“leo”)

 

// 只要某些参数只在函数体里冒出1回,则能够行使下划线 _ 来替换那几个参数

def triple(func: (Int) => Int) = { func(3)
}

triple(3 * _)

 

// 诸如3 *
_的那种语法,必须领悟!!spark源码中山大学量行使了那种语法!

 

某个许个下划线,则就代表某些许个不一样的参数。八个占位符时,第三个下划线表示第一个参数,第一个下划线表示第一个参数,以此类推;所以1律参数多处出现时是无力回天运用那种占位符来表示的。

 

行使占位符时,有时不可能推导出类型,如:

scala> val f = _ + _

那会儿需鲜明写出档次:

scala> val f = (_: Int) + (_: Int)

f: (Int, Int) => Int = <function2>

Scala的常用高阶函数

// map: 对传播的种种成分都进展览放映射,再次回到3个处理后的成分

Array(1, 2, 3, 4, 5).map(2 * _)

 

// foreach: 对传播的各样成分都开始展览拍卖,然则从未再次回到值

(1 to 9).map(“*” * _).foreach(println _)

 

// filter: 对传播的各类成分都进行标准判断,尽管对元素重临true,则保留该因素,否则过滤掉该因素

(1 to 20).filter(_ % 2 == 0)

 

// reduceLeft: 从左侧成分开首,实行reduce操作,即先对成分1和要素二拓展处理,然后将结果与成分3处理,再将结果与成分4拍卖,依次类推,即为reduce;reduce操作必须控制!spark编制程序的根本!!!

// 上边那一个操作就一定于一
* 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9

(1 to 9).reduceLeft( _ * _)

 

// sortWith: 对成分实行两两比照,举行排序

Array(3, 2, 5, 4, 10, 1).sortWith(_ < _)

闭包

// 闭包最精简的演讲:函数在变量不处于其卓有成效功效域时,仍是能够够对变量举办走访,即为闭包

def getGreetingFunc(msg: String) = (name: String)
=> println(msg + “, ” + name)

val greetingFuncHello =
getGreetingFunc(“hello”)

val greetingFuncHi = getGreetingFunc(“hi”)

// 一遍调用getGreetingFunc函数,传入不一致的msg,并成立不一样的函数重临

// 可是,msg只是叁个有些变量,却在getGreetingFunc执行完今后,仍是可以够再而三存在创建的函数之中;greetingFuncHello(“leo”),调用时,值为”hello”的msg被保留在了函数体内部,能够频仍的运用

// 那种变量超出了其功用域,仍是能够使用的情事,即为闭包

// Scala通过为各样函数创造对象来贯彻闭包,实际上对于getGreetingFunc函数制造的函数,msg是当做函数对象的变量存在的,因而各类函数才方可拥有分裂的msg

SAM转换

万1Scala调用Java的某部方法传入的是2个SAM,则足以通过Scala提供的很有利的一种转移,将函数对象会传给Java方法

 

// 在Java中,由于不扶助直接将函数字传送入三个办法作为参数,平常来说,唯1的情势正是概念3个完结了有些接口的类的实例对象,该指标唯有贰个措施,犹如那样接口唯有单个的肤浅方法,就叫single abstract method,简称为SAM

 

// 由于Scala是能够调用Java的代码的,由此当我们调用Java的某部方法时,或许就只可以成立SAM传递给艺术,卓殊辛苦;不过Scala又是支撑直接传递函数的。此时就能够使用Scala提供的,在调用Java方法时,使用Scala提供的SAM转换职能,即将SAM转换为Scala函数

 

// 要使用SAM转换,须要利用Scala提供的表征,隐式转换

import javax.swing._

import java.awt.event._

val button = new JButton(“Click”)

button.addActionListener(new ActionListener {//
ActionListener接口只有三个空洞方法,这样的接口叫SAM

  override def actionPerformed(event: ActionEvent)
{

    println(“Click Me!!!”)

  }

})

 

implicit def
getActionListener(actionProcessFunc: (ActionEvent) => Unit) = new
ActionListener {

  override def actionPerformed(event: ActionEvent)
{

    actionProcessFunc(event)

  }

}

button.addActionListener((event: ActionEvent) =>
println(“Click Me!!!”))

Currying函数

// Curring函数,指的是,将原来接收三个参数的三个函数,转换为四个函数,第3个函数接收原先的率先个参数,然后回到接收原先第一个参数的第二个函数。

// 在函数调用的历程中,就改为了三个函数两次三番调用的款型

// 在斯Parker的源码中,也有呈现,所以对()()那种情势的Curring函数,必须控制!

def sum(a: Int, b: Int) = a + b

sum(1, 1)

 

def sum2(a: Int) = (b: Int) => a + b

sum2(1)(1)

 

def sum3(a: Int)(b: Int) = a + b

sum2(1)(1)

return到外围函数

// Scala中,不供给选取return来回到函数的值,函数最终1行语句的值,就是函数的重临值。在Scala中,return用于在匿名函数中重返值给带有匿名函数的带名函数(即外层函数),并作为带名函数的再次回到值。

// 使用return的匿名函数,是必须交给重临类型的,否则无法通过编写翻译

def greeting(name: String) = {

  def sayHello(name: String):String = {

    return “Hello, ” + name

  }

  sayHello(name)

}

greeting(“leo”)

Scala的聚众种类布局

// Scala中的集合种类首要总结:Iterable、Seq、Set、Map。在那之中Iterable是装有集合trait的根trait。那些结构与Java的汇集连串相当相像(最上层为public interface Collection<E> extends
Iterable<E>)。

// Scala中的集合是分成可变和不可变两类集合的,在这之中可变集合正是说,集合的成分得以动态修改,而不可变集合的因素在开头化之后,就不能够修改了。分别对应scala.collection.mutable和scala.collection.immutable多个包。

// Seq下富含了Range、ArrayBuffer、List等子trait。个中Range就象征了1个行列,常常能够动用“1 to 10”那种语法来发出2个Range。 ArrayBuffer就象是于Java中的ArrayList。

List

// List代表3个不可变的列表

// List的创建,val list = List(1, 2, 3,
4)

// List有head和tail,head表示List的首先个要素,tail意味着第一个因素之后的具备因素,list.head,list.tail

// List有异乎经常的::操作符,能够用来将head和tail合并成二个List,0 :: list

// ::那种操作符要清楚,在spark源码中都是有展现的,一定要可以看懂!

// 假若二个List只有2个成分,那么它的head就是那一个因素,它的tail是Nil

// 案例:用递归函数来给List中种种成分都抬高钦点前缀,并打字与印刷加上前缀的成分

def decorator(l: List[Int], prefix: String)
{

  if (l != Nil) {

    println(prefix + l.head)

    decorator(l.tail, prefix)

  }

}

LinkedList

// LinkedList代表2个可变的列表,使用elem能够引用其头顶,使用next可以引用其尾巴部分

// val l = scala.collection.mutable.LinkedList(1, 2,
3, 4, 5); l.elem; l.next

// 案例:使用while循环将LinkedList中的每一个成分都乘以2

val list = scala.collection.mutable.LinkedList(1, 2,
3, 4, 5)

var currentList = list

while (currentList != Nil) {

  currentList.elem = currentList.elem * 2

  currentList = currentList.next

}

// 案例:使用while循环将LinkedList中,从第一个成分起首,每隔二个成分,乘以2

val list = scala.collection.mutable.LinkedList(1, 2,
3, 4, 5, 6, 7, 8, 9, 10)

var currentList = list

var first = true

while (currentList != Nil && currentList.next != Nil)
{

  if (first) { currentList.elem = currentList.elem *
2; first = false }

  currentList  = currentList.next.next

  if (currentList != Nil) currentList.elem =
currentList.elem * 2

}

Set

// Set代表多个尚未再次成分的聚集,Set为trait,分为可变与不足变二种trait

// 将再一次成分参加Set是从未用的,比如val s
= Set(一, 二, 3); s + 1; s + 4

// 而且Set是不保险插入顺序的,也正是说,Set中的成分是乱序的,val
s = new scala.collection.mutable.HashSet[Int](); s += 1; s += 2; s +=
5

// LinkedHashSet会用二个链表维护插入顺序,val s = new
scala.collection.mutable.LinkedHashSet[Int](); i += 1; s += 2; s +=
5

// SrotedSet会自行依据key来展开排序,val s =
scala.collection.mutable.SortedSet(“orange”, “apple”, “banana”)

集合的函数式编制程序

// 集合的函数式编程至极可怜可怜之首要性!!!

// 必须完全精通和清楚Scala的高阶函数是何等意思,Scala的集合类的map、flatMap、reduce、reduceLeft、foreach等这个函数,正是高阶函数,因为能够收到别的函数作为参数

// 高阶函数的行使,也是Scala与Java最大的某个两样!!!因为Java里面是未有函数式编制程序的,也必定没有高阶函数,也肯定不能直接将函数字传送入三个主意,只怕让三个主意重返八个函数

// 对Scala高阶函数的明亮、了然和选用,能够大大抓牢你的技艺,而且也是Scala最有魔力、最有优势的1个效率!!!

// 别的,在斯Parker源码中,有大批量的函数式编制程序,以及基于集合的高阶函数的运用!!!所以必须控制,才能看懂spark源码

 

// map案例实战:为List中种种成分都添加二个前缀

List(“Leo”, “Jen”, “Peter”, “Jack”).map(“name is ” +
_)

 

// faltMap案例实战:将List中的多行句子拆分成单词

List(“Hello World”, “You Me”).flatMap(_.split(”
“))

 

// foreach案例实战:打字与印刷List中的每一种单词

List(“I”, “have”, “a”, “beautiful”,
“house”).foreach(println(_))

 

// zip案例实战:对学员姓名和学习者战绩举行关联

List(“Leo”, “Jen”, “Peter”, “Jack”).zip(List(100, 90,
75, 83))

函数式编制程序综合案例:总结五个公文内的单词总数

// 使用scala的io包将文件文件内的数据读取出来

val lines01 =
scala.io.Source.fromFile(“C://Users//Administrator//Desktop//test01.txt”).mkString

val lines02 =
scala.io.Source.fromFile(“C://Users//Administrator//Desktop//test02.txt”).mkString

 

// 使用List的伴生对象,将八个公文内的始末创设为三个List

val lines = List(lines01, lines02)

 

// 上边那一行才是大家的案例的着力和要紧,因为有七个高阶函数的链式调用,以及大气下划线的利用,假设未有透彻通晓在此以前的课教学的Scala函数式编程,那么下边那1行代码,完全或然会看不懂!!!

// 不过上边这行代码其实正是Scala编程的精髓所在,便是函数式编程,也是Scala相较于Java等编制程序语言最大的效率优势所在

// 而且,spark的源码中山高校量接纳了那种复杂的链式调用的函数式编程

// 而且,spark自身提供的开发人士使用的编制程序api的作风,完全沿用了Scala的函数式编制程序,比如斯Parker自个儿的api中就提供了map、flatMap、reduce、foreach,以及越来越尖端的reduceByKey、groupByKey等高阶函数

// 要是要动用Scala进行spark工程的成本,那么就务须控制那种复杂的高阶函数的链式调用!!!

 

lines.flatMap(_.split(” “)).map((_,
1)).map(_._2).reduceLeft(_ + _)

格局相配

// Scala是绝非Java中的switch
case语法的,相对应的,Scala提供了特别有力的match
case语法,即情势相配,类替代switch case,match
case也被号称方式匹配

 

// Scala的match case与Java的switch
case最大的分化点在于,Java的switch case仅能相配变量的值,比一、二、三等;而Scala的match case能够包容各个处境,比如变量的品类、集合的因素、有值或无值

 

// match case的语法如下:变量** match { case
值 =>
代码
}**。固然值为下划线,则意味着了不满意以上全部情形下的暗中认可处境如何处理。其它,match case中,只要3个case分支满足并处理了,就不会持续判断下2个case分支了。(与Java分裂,java的switch case要求用break阻止)

// match case语法最大旨的使用,正是对变量的值进行方式相配

// 案例:战表评价

def judgeGrade(grade: String) {

  grade match {

    case “A” => println(“Excellent”)

    case “B” => println(“Good”)

    case “C” => println(“Just so so”)

    case _ => println(“you need work
harder”)

  }

}

在方式匹配中运用if守卫

// Scala的情势相称语法,有一脾性情在于,能够在case后的尺码判断中,不仅仅只是提供三个值,而是能够在值前面再加一个if守卫,举办重复过滤

// 案例:成绩评价(升级版)

def judgeGrade(name: String, grade: String)
{

  grade match {

    case “A” => println(name + “, you are
excellent”)

    case “B” => println(name + “, you are
good”)

    case “C” => println(name + “, you are just so
so”)

    case _ if name ==
“leo” => println(name + “, you are a good boy, come on”)

    case _ => println(“you need to work
harder”)

  }

}

在格局相配中进行变量赋值

// Scala的方式相配语法,有3个特点在于,可以将形式匹配的默许情状,将下划线替换为一个变量名,此时形式相称语法就会就要相配的值赋值给这些变量,从而能够在后边的处理语句中选择要同盟的值

 

// 为啥有那种语法??思索一下。因为借使采用用case相称到的值,是否大家就精通那么些只啦!!在那些case的处理语句中,是否就径直能够使用写程序时就已知的值!

 

// 可是对于下划线_那种情景,全数不满足前边的case的值,都会进去_那种暗中认可情况展开处理,此时只要大家在拍卖语句中要求获得实际的值实行处理吧?那就要求利用那种在格局相配中展开变量赋值的语法!!

 

// 案例:成绩评价(升级版)

def judgeGrade(name: String, grade: String) {

  grade match {

    case “A” => println(name + “, you are
excellent”)

    case “B” => println(name + “, you are
good”)

    case “C” => println(name + “, you are just so
so”)

    case grade_ if
name == “leo” => println(name + “, you are a good boy, come on, your
grade is ” + grade+  ” : ” + grade_)

    case _ =>
println(“you need to work harder, your grade is ” + grade)

  }

}

对品种进行形式相称

// Scala的形式相称多个精锐之处就在于,能够直接般配类型,而不是值!!!这一点是java的switch case绝对做不到的。

 

// 理论知识:对项目怎样进展相配?别的语法与相称值其实是均等的,不过相称类型的话,正是要用“case 变量: 类型 =>
代码”那种语法,而不是相配值的“case 值 => 代码”那种语法。

 

// 案例:分外处理

import java.io._

def processException(e: Exception) {

  e match {

    case e1: IllegalArgumentException =>
println(“you have illegal arguments! exception is: ” + e1)

    case e2: FileNotFoundException =>
println(“cannot find the file you need read or write!, exception is: ” +
e2)

    case e3: IOException => println(“you got an
error while you were doing IO operation! exception is: ” + e3)

    case _: Exception => println(“cannot know
which exception you have!” )

  }

}

processException(new IOException (“File not
found”))

对Array和List举办情势相称

// 对Array举行方式相称,分别能够相配带有钦定成分的数组、带有钦点个数成分的数组、以某成分打头的数组

// 对List进行格局匹配,与Array类似,不过要求选取List特有的::操作符

// 案例:对朋友打招呼

def greeting(arr: Array[String]) {

  arr match {

    case Array(“Leo”) => println(“Hi,
Leo!”)

    case Array(girl1, girl2, girl3) =>
println(“Hi, girls, nice to meet you. ” + girl1 + ” and ” + girl2 + ”
and ” + girl3)

    case Array(“Leo”, _*) => println(“Hi, Leo,
please introduce your friends to me.”)

    case _ => println(“hey, who are you?”)

  }

}

greeting(Array(“Leo”,”Jack”))

 

def greeting(list: List[String]) {

  list match {

    case “Leo” :: Nil => println(“Hi,
Leo!”)

    case girl1 :: girl2 :: girl3 :: Nil =>
println(“Hi, girls, nice to meet you. ” + girl1 + ” and ” + girl2 + ”
and ” + girl3)

    case “Leo” :: tail => println(“Hi, Leo, please
introduce your friends to me.”)

    case _ => println(“hey, who are you?”)

  }

}

greeting(List(“Leo”,”Jack”))

case class与方式相称

// Scala中提供了一种特别的类,用case
class实行宣示,粤语也足以称作样例类。case class其实有点类似于Java中的JavaBean的定义。即只定义田野,并且由Scala编写翻译时自动提供getter和setter方法,不过从未method。

 

// case class的主构造函数收取的参数平时不需求使用var或val修饰,Scala自动就会动用val修饰(然而一旦你协调使用var修饰,那么依旧会依照var来)

 

//  Scala自动为case class定义了伴生对象,约等于object,并且定义了apply()方法,该措施接收主构造函数中相同的参数,并再次回到case class对象

// 案例:高校门禁

class Person

case class Teacher(name: String, subject: String)
extends Person

case class Student(name: String, classroom: String)
extends Person

def judgeIdentify(p: Person) {

  p match {

    case Teacher(name, subject) =>
println(“Teacher, name is ” + name + “, subject is ” + subject)

    case Student(name, classroom) =>
println(“Student, name is ” + name + “, classroom is ” +
classroom)

    case _ => println(“Illegal access, please go
out of the school!”)

  } 

}

judgeIdentify(Student(“Leo”,”1″))

Option与情势相配

// Scala有一种特其余花色,叫做Option。Option有三种值,壹种是Some,表示有值,壹种是None,表示并未值。

// Option经常会用来情势相称中,用于判断某些变量是有值依旧不曾值,那比null来的愈来愈简洁明了

// Option的用法必须了然,因为斯Parker源码中山大学量地动用了Option,比如Some(a)、None那种语法,由此必须看得懂Option方式相配,才能够读懂spark源码。

// 案例:成绩查询

val grades = Map(“Leo” -> “A”, “Jack” -> “B”,
“Jen” -> “C”)

def getGrade(name: String) {

  val grade = grades.get(name)

  grade match {

    case Some(grade1) => println(“your grade is “

  • grade1)

    case None => println(“Sorry, your grade
information is not in the system”)

  }

}

getGrade(“Lily”)

getGrade(“Leo”)

 

Scala集合类的壹些正式操作会产生Option可选值,如Map的get方法,查到值时归来Some(value)对象,没查到时重回None对象(而Java中回到的为Null,那会简单造成程序运维错误)

花色参数

品种参数是怎么样?类型参数其实就象是于Java中的泛型。先说说Java中的泛型是何等,比如我们有List a = new ArrayList(),接着a.add(1),没难点,a.add(“贰”),然后我们a.get(壹) ==
二,对不对?肯定不对了,a.get(一)获取的骨子里是个String——”2″,String——”2″怎么可能与二个Integer类型的二相当于呢?

 

据此Java中提议了泛型的概念,其实也便是体系参数的定义,此时能够用泛型成立List,List a = new ArrayList[Integer](),那么,此时a.add(一)没难题,而a.add(“二”)呢?就老大了,因为泛型会限制,只好往集合中添加Integer类型,那样就幸免了上述的题材。

 

那正是说Scala的档次参数是什么样?其实意思与Java的泛型是同等的,也是概念一种档次参数,比如在汇聚,在类,在函数中,定义类型参数,然后就足以确定保证使用到该类型参数的地点,就肯定,也只可以是那体系型。从而完成程序更加好的健壮性。

 

除此以外,类型参数是斯Parker源码中非凡常见的,由此等同必须控制,才能看懂spark源码。

泛型类

// 泛型类(类表明时类名前边中括号中的即为类型参数),顾名思义,其实便是在类的宣示中,定义1些泛型类型,然后在类内部,比如田野或然method,就足以选取这一个泛型类型。

// 使用泛型类,经常是须求对类中的有个别成员,比如壹些田野同志和method中的参数或变量,举办联合的项目限制,那样能够确定保证程序越来越好的健壮性和伊春久安。

// 倘使不行使泛型进行统壹的连串限制,那么在晚期程序运维进度中,难免会出现难题,比如传入了不愿意的类型,导致程序出难题。

// 在运用类的时候,比如创制类的目的,将品种参数替换为实际的档次,即可。

案例:新生报到,每一个学生来自不一致的地方,id恐怕是Int,恐怕是String

class Student[T](val
localId: T) { // 在类参数中运用项目参数

  def getSchoolId(hukouId: T) = “S-” + hukouId + “-“

  • localId // 在艺术参数中接纳项目参数

}

val leo = new Student[Int](111)

 

// Scala自动测算泛型类型天性:直接给使用了泛型类型的田野先生赋值时,Scala会自动进行项目估计。

scala> val leo = new Student(111)

leo: Student[Int] =
Student@f001896

scala> val leo = new Student(“string”)

leo: Student[String]
= Student@488eb7f2

泛型函数

// 泛型函数(方法评释时方法名前面中括号中的即为类型参数),与泛型类类似,能够给有个别函数在表明时钦定泛型类型,然后在函数体内,八个变量只怕重返值之间,就足以行使泛型类型实行宣示,从而对某些特殊的变量,恐怕三个变量,实行强制性的花色限制。

案例:卡片售卖机,能够钦命卡片的剧情,内容能够是String类型或Int类型

def getCard[T](content: T) = {

  if(content.isInstanceOf[Int]) “card: 001, ” +
content

  else if(content.isInstanceOf[String]) “card: this
is your card, ” + content

  else “card: ” + content

}

getCard[String](“hello world”)

getCard[Double](0.01)

 

// 与泛型类一样,你能够由此给使用了泛型类型的变量传递值来让Scala自动测算泛型的莫过于类型,也足以在调用函数时,手动钦赐泛型类型,上边正是在调用时手动在中括号中钦点的,上面靠传入值自动测算:

scala> getCard (“hello world”)

res2: String = card: this is your card, hello
world

scala> getCard (0.01)

res3: String = card: 0.01

上边界Bounds

// 在内定泛型类型的时候,有时,我们须求对泛型类型的限制实行限制,而不是能够是随意的体系。比如,大家恐怕供给有个别泛型类型,它就不可能不是有个别类的子类,那样在程序中就足以放心地调用泛型类型继承的父类的艺术,程序才能正常的使用和运营。此时就足以应用前前面界Bounds的表征。如下边未有利用上面界时,是不可能调用类型参数相关措施的:

class Person(val name: String) {

  def makeFriends(p: Person) {}

}

class Party[T](p1: T, p2: T) {

 def play = p一.makeFriends(p贰) // 编写翻译会出错

}

 

// Scala的前后面界特性允许泛型类型必须是某些类的子类,或然必须是某些类的父类

 

案例:在派对缴纳朋友

class Person(val name: String) {

  def sayHello = println(“Hello, I’m ” + name)

  def makeFriends(p: Person) {

    sayHello

    p.sayHello

  }

}

class Student(name: String) extends Person(name)

class Party[T
<:
Person](p1: T, p2: T) { // <:须要T必须是Person或其子类,由于p一 类型为Person或其子类Student,所以能够调用父类中的方法

  def play = p1.makeFriends(p2)

}

 

val leo = new Student(“Leo”)

val lily = new Student(“Lily”)

new Party(leo,lily).play

下边界Bounds

//
除了内定泛型类型的上面界,还是能够钦赐上面际,即内定泛型类型必须是某个类的父类

 

案例:领身份证(只可以是自身或老爸代领)

class
Father(val name: String)

class
Child(name: String) extends Father(name)

def
getIDCard[R >: Child](person:
R) { // R必须是Child的父类

  if
(person.getClass == classOf[Child]) println(“please tell us your
parents’ names.”)

  else if
(person.getClass == classOf[Father]) println(“sign your name for your
child’s id card.”)

  else
println(“sorry, you are not allowed to get id card.”)

}

 

val f = new
Father(“Father”)

val c = new
Child(“Child”)

scala>
getIDCard[Father](f)

sign your name
for your child’s id card.

scala>
getIDCard[Child](c)

please tell us
your parents’ names.

 

scala>
getIDCard(f) // 类型自动测算

sign your name
for your child’s id card.

scala>
getIDCard(c) // 类型自动测算

please tell us
your parents’ names.

 

val s = new
Student(“Student”)

scala>
getIDCard(s)

sorry, you are
not allowed to get id card.

 

注:下面际与下面界是见仁见智的,上面界是为了可以调用类型参数相应措施,而上边际是为了限制泛型类或泛型函数只适用于如何类,而不是为着调用类型参数相应措施。

View Bounds

//
上下面界Bounds,即便能够让一种泛型类型,帮助有父子关系的各样类型。但是,在某些类与上上面界Bounds内定的老爹和儿子类型范围内的类都未曾别的涉及,则默许是早晚无法承受的。

 

//
可是,View
Bounds作为壹种左右侧界Bounds的抓牢版,辅助可以对项目举办隐式转换,将点名的门类举行隐式转换后,再判断是或不是在边际钦点的项目范围内

 

案例:跟黄狗交朋友

class
Person(val name: String) {

  def sayHello
= println(“Hello, I’m ” + name)

  def
makeFriends(p: Person) {

   
sayHello

   
p.sayHello

  }

}

class
Student(name: String) extends Person(name)

class Dog(val
name: String) { def sayHello = println(“Wang, Wang, I’m ” + name)
}

 

implicit def
dog2person(o: Object): Person =

      
if(o.isInstanceOf[Dog]) {println(“-D-“);val _o =
o.asInstanceOf[Dog]; new Person(_o.name){

             
override def sayHello = _o.sayHello

       } }//
即便是狗,隐式的转移为人

      
//注:即便是Person或Student,也要将Object强转成Person后回到,而不能够向来将o重临,不然大概引起循环调用隐式转换

       else
if(o.isInstanceOf[Person]) {println(“-P-“);val _o =
o.asInstanceOf[Person];_o} // 假若是人,不用更换,只是强转型后赶回

       else
{println(“-O-“);error(o.toString)} //别的意况重临Nothing

      

      

class Party[T
<% Person](p一: T){ // <%表示T可以是Person或其子类,恐怕是足以由此隐式转换后化作Person或其子类的类

   def play()
= {println(p1.name);println(“——————–“)}

}

class
Party2[T <% Person](p1:
T,p2: T){

  def play() =
{p1.makeFriends(p2);println(“——————–“)}

}

 

val leo = new
Person(“Leo”)

val lily = new
Student(“Lily”)

new
Party(leo).play()

new
Party(lily).play()

val dog = new
Dog(“Dog”)

new
Party(dog).play()//产生隐式转换

new
Party2(lily,leo).play()

new
Party二(dog,leo).play()//发生隐式转换

new
Party2(lily,dog).play()//产生隐式转换

Context Bounds

// Context Bounds是壹种奇特的Bounds,它会依据泛型类型的宣示,比如“T: 类型”需求必须存在3个档次为“类型[T]”的隐式值(运转时Scala会帮我们自行注入这么些已存在的隐式值)。其实个人认为,Context Bounds之所以叫Context,是因为它依据的是壹种全局的上下文,要求使用到上下文中的隐式值以及注入。

 

案例:使用Scala内置的比较器相比大小

// Ordering[T]类似Java中的Comparator<T>比较器

class Calculator[T: Ordering] (val number1: T, val
number2: T) {

  //运转时,Scala会在上下文中去找项目为Ordering[T]的隐式值并注进来,所以调用该情势时不必要传递该参数值了

  def max(implicit
order: Ordering[T]) = if(order.compare(number1, number2)
> 0) number1 else number2

}

new Calculator(3,4).max

Manifest Context Bounds

// 在Scala中,假设要实例化2个泛型数组,就不能够不运用Manifest Context Bounds。约等于说,即使数组成分类型为T的话,须求为类还是函数定义[T: Manifest]泛型类型,那样才能实例化Array[T]那种泛型数组。

 

案例:打包饭菜(一种食物打成壹包)

class Meat(val name: String)

class Vegetable(val name: String)

 

def packageFood[T: Manifest] (food: T*) = {

  // 创造泛型数组Array[T]:即数组中的成分类型要在运作时才能鲜明,编写翻译时胸中无数明确,成分类型为动态

  val foodPackage = new Array[T](food.length)

  for(i <- 0 until food.length) foodPackage(i) = food(i)

  foodPackage

}

val gongbaojiding = new Meat(“gongbaojiding”)

val yuxiangrousi = new Meat(“yuxiangrousi”)

val shousiyangpai = new Meat(“shousiyangpai”)

val meatPackageFood =
packageFood(gongbaojiding,yuxiangrousi,shousiyangpai)

meatPackageFood: Array[Meat] = Array(Meat@20b829d5, Meat@7c5f29c6,
Meat@4baf997)

 

val qingcai = new Vegetable(“qingcai”)

val baicai = new Vegetable(“baicai”)

val huanggua = new Vegetable(“huanggua”)

val vegPackageFood =
packageFood(qingcai,baicai,huanggua)

vegPackageFood:
Array[Vegetable] =
Array(Vegetable@583030bd, Vegetable@2ac3d530, Vegetable@2431050d)

协变和逆变

// Scala的协变和逆变是老大有风味的!完全缓解了Java中的泛型的一大缺憾!

// 举例来说,Java中,假诺有Professional是Master的子类,那么Card[Professionnal]是不是Card[Master]的子类?答案是:不是。由此对于开发顺序造成了众多的劳动。

// 而Scala中,只要灵活利用协变和逆变,就能够缓解Java泛型的难点。

 

案例:进入会场

class Master // 大师

class Professional extends Master // 专家,按理来说,大师是一种专家,应该是Master为Professional子类才对

 

// 大师以及专家的名片都得以进来会场

class Card[+T] (val name: String) // 协变:当类型B是类型A的子类型,则足以认为T[B]是T[A]的子类

def enterMeet(card: Card[Master]) {

  println(“welcome to have this meeting!”)

}

// 假设去掉+加号,则Card[Professional]不能够传遍到enterMeet方法

 

//专家级别及以上海高校师级别的片子就可以进去会场

class Card[-T] (val name: String) // 逆变:当类型B是类型A的子类型,则反过来能够认为T[A]是T[B]的子类型

// 要想父类也足以传进来,则要让Card实行逆变,那样Card[Master]就转头成为卡德[Professional]的子类,所以就能传进来了

def enterMeet(card: Card[Professional]) {

  println(“welcome to have this meeting!”)

}

Existential Type

// 在Scala里,有一种特其他项目参数,便是Existential Type,存在性类型。那类别型务必领悟是怎样意思,因为在spark源码实在是太普遍了!

Array[T] forSome { type T }

Array[_]

 

 

scala> def foo[T](x : Array[T]) =
println(x.length)

foo: [T](x: Array[T])Unit

scala>  foo(Array[String](“foo”, “bar”,
“baz”))

3

 

scala> def foo(x :
Array[T] forSome { type T}) = println(x.length)

foo: (x: Array[_])Unit

scala> foo(Array[String](“foo”, “bar”,
“baz”))

3

 

scala> def foo(x :
Array[_]) = println(x.length)

foo: (x: Array[_])Unit

scala> foo(Array[String](“foo”, “bar”,
“baz”))

3

 

scala> def foo(x : Array[T] forSome { type T
<: CharSequence}) = x.foreach(y => println(y.length))

foo: (x: Array[_ <: CharSequence])Unit

scala> foo(Array[String](“foo”, “bar”,
“baz”))

3

3

3

隐式转换

Scala提供的隐式转换和隐式参数功用,是可怜有特色的职能。是Java等编制程序语言研商所未有的功用。它可以允许你手动钦点,将某系列型的指标转换来其余类型的指标。通过那几个意义,能够兑现丰盛有力,而且相当的效劳。

 

Scala的隐式转换,其实最中央的正是概念隐式转换函数,即implicit conversion function。定义的隐式转换函数,只要在编辑的程序内引入,就会被Scala自动使用。Scala会根据隐式转换函数的签订契约,在程序中利用到隐式转换函数接收的参数类型定义的靶鸡时,会自行将其传播隐式转换函数,转换为此外1种档次的目的并赶回。那正是“隐式转换”。

 

隐式转换函数叫什么名字是漠不关怀的,因为壹般而言不会由用户手动调用,而是由Scala举办调用。可是如若要运用隐式转换,则必要对隐式转换函数进行导入。由此普通建议将隐式转换函数的称谓命名字为“one二one”的花样。

 

斯Parker源码中有雅量的隐式转换和隐式参数,因此必须明白那种语法。

 

// 要达成隐式转换,只要程序可见的限量内定义隐式转换函数即可。Scala会自动使用隐式转换函数。隐式转换函数与日常函数唯壹的语法不同便是,要以implicit开端,而且最棒要定义函数重临类型。

 

// 案例:特殊领票窗口(只接受特殊人群,比如学生、老人等)

class SpecialPerson(val name: String)

class Student(val name: String)

class Older(val name: String)

implicit def
object2SpecialPerson (obj: Object): SpecialPerson = {

  if (obj.getClass == classOf[Student]) { val stu =
obj.asInstanceOf[Student]; new SpecialPerson(stu.name) }

  else if (obj.getClass == classOf[Older]) { val
older = obj.asInstanceOf[Older]; new SpecialPerson(older.name)
}

  else
error(obj.toString)

}

var ticketNumber = 0

def buySpecialTicket(p: SpecialPerson) =
{//只针对特殊人群卖票,所以只如果学员与老人,则要先转移为特殊人群

  ticketNumber += 1

  “T-” + ticketNumber

}

 

class Teacher(val name:String)

scala> val tom = new Teacher(“tom”)

scala> buySpecialTicket(tom)

java.lang.RuntimeException: Teacher@277f7dd3

  at
scala.sys.package$.error(package.scala:27)

  at scala.Predef$.error(Predef.scala:144)

  at .object2SpecialPerson(<console>:17)

  … 32 elided

 

scala> val s = new Student(“student”)

scala> val o = new Older(“older”)

scala> buySpecialTicket(s)

res1: String = T-1

scala> buySpecialTicket(o)

res2: String = T-2

动用隐式转换抓实现有项目

// 隐式转换分外强大的二个职能,就是能够在无意识中升高现有项目标意义(有点像Java中的装饰情势)。也等于说,能够为有个别类定义3个坚实版的类,并定义互相之间的隐式转换,从而让源类在使用加强版的章程时,由Scala自动举行隐式转换为增高类,然后再调用该措施(如放置的Int 类的抓牢版
RichInt)。

 

// 案例:超人变身

class Man(val name: String)

class Superman(val name: String) {

  def emitLaser = println(“emit a laster!”) //
超人才有该方法

}

implicit def man2superman(man: Man): Superman = new
Superman(man.name)

val leo = new Man(“leo”) // 普通人

leo.emitLaser // 调用不存在的秘诀时,会自动的先转移为独立

隐式转换函数效用域与导入

// Scala暗中同意有二种找隐式转换的方法:首先是从源类型恐怕指标项目,那两类其他伴生对象中找隐式转换函数;然后在当前先后功效域内找隐式转换函数。

 

// 如若隐式转换函数不在上述二种状态下的话,那么就不能不手动使用import语法引进某些包下的隐式转换函数,比如import test._。常常提出,仅仅在要求开始展览隐式转换的地点,比如有个别函数或然措施内,用import导入隐式转换函数,那样能够裁减隐式转换函数的作用域,防止不须要的隐式转换。

隐式转换的发生时机

// 一、调用某些函数,但是给函数传入的参数的类型,与函数定义的接受参数类型不合作(案例:特殊领票窗口

// 二、使用有些项指标对象,调用有些方法,而那么些主意并不存在于此类型时(案例:超人变身

// 三、使用某些项指标指标,调用有些方法,即使该品种有其一点子,不过传给方法的参数类型与措施定义的接收参数的连串不相配(案例:特殊购票窗口抓好版)

//4、将1种档次赋值给另壹种档次时,如若类型不配合时

 

// 案例:特殊购票窗口抓好版

class TicketHouse {

  var ticketNumber = 0

  def buySpecialTicket(p: SpecialPerson) = {

    ticketNumber += 1

    “T-” + ticketNumber

  }

}

new TicketHouse().buySpecialTicket(new
Student(“student”))

隐式参数

// 所谓的隐式参数,指的是在函数恐怕措施中,定义三个用implicit修饰的参数,此时Scala会尝试找到3个钦命项目标,用implicit修饰的目的,即隐式值,并自动注入到该隐式参数。

 

// Scala会在五个范围内搜寻:壹种是日前效果域钦赐义val或var隐式变量;壹种是从隐式参数类型的伴生对象内找隐式值

 

// 案例:考试签到

class SignPen {

  def write(content: String) =
println(content)

}

implicit val signPen = new SignPen

def signForExam(name: String) (implicit signPen: SignPen) {

  signPen.write(name + ” come to exam in
time.”)

}

Actor

Scala的Actor类似于Java中的四线程编制程序。可是不一致的是,Scala的Actor提供的模型与八线程有所差别。Scala的Actor尽可能地幸免锁和共享状态,从而防止三十二线程并发时出现能源争用的景色,进而升级多线程编制程序的性质。其余,Scala Actor的那种模型还是可以免止死锁等1多级古板八线程编制程序的难点。

 

斯Parker中行使的分布式三十二线程框架,是Akka。Akka也落到实处了近乎Scala
Actor的模子,其大旨概念壹样也是Actor。由此只要明白了Scala
Actor,那么在Spark源码切磋时,至少即可看明白Akka Actor相关的代码。可是,换一句话说,由于斯Parker内部有恢宏的Akka
Actor的采取,由此对此Scala Actor也至少必须控制,那样才能学习斯Parker源码。

Actor的始建、运维和新闻收发

// Scala提供了Actor trait来让大家更有益于地开始展览actor二十四线程编制程序,就Actor
trait就像于Java中的Thread和Runnable一样,是基础的二十八线程基类和接口。大家只要重写Actor trait的act方法,即可实现和谐的线程执行体,与Java中重写run方法类似。

// 别的,使用start()方法运转actor;使用!符号,向actor发送音讯;actor内部使用receive和情势相称接收新闻

// 案例:Actor Hello
World

import scala.actors.Actor

class HelloActor extends Actor {

  def act() {

    while (true) {

      receive {

        case name: String => println(“Hello, ” +
name)

      }

    }

  }

}

val helloActor = new HelloActor

helloActor.start()

helloActor ! “leo”

收发case class类型的音讯

// Scala的Actor模型与Java的多线程模型之间,不小的3个分别正是,Scala Actor天然援助线程之间的精准通讯;即多少个actor可以给其余actor直接发送消息。那一个功用是可怜有力和便利的。

// 要给1个actor发送音信,必要运用“actor ! 消息”的语法。在scala中,日常提议利用样例类,即case class来作为消息举行发送。然后在actor接收音信随后,能够采纳scala强大的方式相配功能来进行分歧新闻的拍卖。

// 案例:用户注册登录后台接口

import scala.actors.Actor

case class Login(username: String, password:
String)

case class Register(username: String, password:
String)

class UserManageActor extends Actor {

  def act() {

    while (true) {

      receive {

        case Login(username, password) =>
println(“login, username is ” + username + “, password is ” +
password)

        case Register(username, password) =>
println(“register, username is ” + username + “, password is ” +
password)

      }

    }

  }

}

val userManageActor = new UserManageActor

userManageActor.start()

userManageActor ! Register(“leo”, “1234”);
userManageActor ! Login(“leo”, “1234”)

Actor之间互相收发新闻

// 要是七个Actor之间要相互收发音信,那么scala的提议是,一个actor向其余四个actor发送音讯时,同时带上自个儿的引用;其余actor收到本身的消息时,直接通过发送音信的actor的引用,即能够给它过来音信。

// 案例:打电话

import scala.actors.Actor

case class Message(content: String, sender:
Actor)

class LeoTelephoneActor extends Actor {

  def act() {

    while (true) {

      receive {

        case Message(content, sender) => {
println(“leo telephone: ” + content); sender ! “I’m leo, please call me
after 10 minutes.” }

      }

    }

  }

}

class JackTelephoneActor(val leoTelephoneActor:
Actor) extends Actor {

  def act() {

    leoTelephoneActor ! Message(“Hello, Leo, I’m
Jack.”, this)

    receive {

      case response: String => println(“jack
telephone: ” + response)

    }

  }

}

val leoTel = new LeoTelephoneActor

val jackTel = new JackTelephoneActor(leoTel)

leoTel.start

jackTel.start

手拉手音讯和Future

// 暗许情状下,音讯都是异步的;不过倘诺期望发送的信息是一同的,即对方接受后,一定要给协调回到结果,那么能够选用!?的章程发送音讯。即val
reply = actor !? message。

 

// 借使要异步发送叁个消息,但是在后续要取得消息的重临值,那么可以利用Future。即!!语法。val future =
actor !! message。val reply = future()。

 

 

附属类小部件列表

 

相关文章