Scala 学习

前言

scala是以实现scaleable language为初衷设计出来的一门语言。官方中,称它是object-oriented language和functional language的混合式语言。并且,scala可以和java程序无缝拼接,因为scala文件编译后也是成为.class文件,并且在JVM上运行。不过,我更关心的是它的scaleable(扩展性)。一门语言到底怎样才算有扩展性呢?对语言而言什么是它的扩展性呢?

个人拙见,认为语言的扩展性可能包括两个方面:

1、语言本身的扩展性

2、由此语言写出来的程序的扩展性

对于第一点,在scala的介绍中提到有些developer可以用scala来定义自己的Domain Specific Language。我想,这一点也许就体现了scala语言本身的扩展性,即它有潜力可以被加工为另一门可用在专门领域中的语言(或许是通过增加某些模型,或某些操作等)。

对于第二点,感触最深的就是马上要见到的Tuple。曾经在做一个项目时,希望一个函数可以多返回两个参数,而不得不重新定义一个JavaBean(继承了它的前辈以保留之前的其他返回值),但是这样导致上层代码一片混乱,一会儿是调用原先的BaseBean,一会儿又是调用新的Bean。如果Java中也能有类似Tuple的东西就太方便了。

文中将描述三个内容:

Tuple -可以将不同类型的数据存储在一个数组中

singleton objects - scala中没有静态方法和属性,全部由singleton object(单例对象)来替代

trait - scala中的类interface物,但是可以拥有方法体,并且可以在类实例化时混入,而不需为了包装某个类而产生子类

 

Tuples
Like Lists, tuples are immutable, but unlike Lists, tuples can contain different types of elements. Thus whereas a list might be a List[Int] or a List[String], a tuple could contain both an Int and a String at the same time. Tuples are very useful, for example, if you need to return multiple objects from a method. Whereas in Java, you would often create a JavaBean-like class to hold the multiple return values, in Scala you can simply return a tuple.

val pair = (99, "Luftballons")
println(pair._1)
println(pair._2)

输出:
99
Luftballons

Understand classes and singleton objects

复制代码
// In greetSimply.scala

class SimpleGreeter {
  val greeting = "Hello, world!"
  def greet() = println(greeting)
}

val g = new SimpleGreeter
g.greet()
复制代码

as in Java, classes in Scala encapsulate fields and methods. Fields are defined with either val or var. Methods are defined with def

Although classes in Scala are in many ways similar to Java, in several ways they are quite different

① One difference between Java and Scala involves constructors.

In Java, classes have constructors, which can take parameters, whereas in Scala, classes can take parameters directly. The Scala notation is more concise—class parameters can be used directly in the body of the class; there’s no need to define fields and write assignments that copy constructor parameters into fields.

复制代码
// In greetFancily.scala

class FancyGreeter(greeting: String) {
  def greet() = println(greeting)
}

val g = new FancyGreeter("Salutations, world")
g.greet
复制代码

但是这样写的一个特点是greeting参数是常量,不能在FancyGreeter内部重新赋值。

② Another area in which Scala departs from Java is that you can't have any static fields or methods in a Scala class.

Instead, Scala allows you to create singleton objects using the keyword object. A singleton object cannot, and need not, be instantiated with new. It is essentially automatically instantiated the first time it is used, and as the “singleton” in its name implies, there is ever only one instance. A singleton object can share the same name with a class, and when it does, the singleton is called the class's companion object. The Scala compiler transforms the fields and methods of a singleton object to static fields and methods of the resulting binary Java class.

复制代码
// In WorldlyGreeter.scala

// The WorldlyGreeter class
class WorldlyGreeter(greeting: String) {
  def greet() = {
    val worldlyGreeting = WorldlyGreeter.worldify(greeting)
    println(worldlyGreeting)
  }
}

// The WorldlyGreeter companion object
object WorldlyGreeter {
  def worldify(s: String) = s + ", world!"
}
复制代码

如果没有相对应的class,而是只有一个object的话,则称为stand-alone. object

复制代码
// In WorldlyApp.scala
// A singleton object with a main method that allows
// this singleton object to be run as an application
object WorldlyApp {
  def main(args: Array[String]) {
    val wg = new WorldlyGreeter("Hello")
    wg.greet()
  }
}
复制代码

a singleton object is either a companion or a stand-alone object. 

③ java文件必须要以文件内的class为名,但是scala却不需要。

不过尽管如此,编者还是希望使用class名为scala文件名,因为这样更便于其他程序员查找所需的文件。
由此,scala文件将分为两种,一种是class的,一种是script的。Class型的文件是不能直接运行的,script的可以。Script语句必须以执行语句作为文件的结尾。

scala WorldlyGreeter.scala # This won't work!

WorldlyGreeter.scala文件必须经由WorldlyApp.scala文件才能带动起来,因而需要将这两个文件编译到一起。可以用scalac指令来完成这一任务:

scalac WorldlyApp.scala WorldlyGreeter.scala

由于每次执行scalac指令的时候,都会产生一个JVM的实例。而JVM在启动时会有明显的能让人察觉的延迟,因此可以考虑使用fsc指令来代替scalac指令。第一次执行fsc指令时,它会产生一个守护进程,并且将此进程与本机的一个端口号绑定。所有需要编译的文件将通过这个端口号传给守护进程。这个进程不会停止直到你使用指令fsc - shutdown人为关闭为止。这样,除了在第一次使用时会有延迟外,其他时候都会马上编译给定的文件而不需要再次启动JVM。

 
Understand traits and mixins

Trait有点类似于java中的interface,但也有不同之处。
①java的interface只定义方法名称和参数列表,不能定义方法体。而trait则可以定义方法体。

trait Friendly {
  def greet() = "Hi"
}

②在java中实现接口用implement,而在scala中,实现trait用extends。

Scala中不再有implement这个关键词。但类似的,scala中的class可以继承0至多个traits。

class Dog extends Friendly {
  override def greet() = "Woof"
}

此处,需要注意的一点是,与java不同,在scala中重写一个方法是需要指定override关键词的。如果重写一个方法时,没有加上override关键词,那么scala编译会无法通过。

③ java的interface和scala的trait的最大区别是,scala可以在一个class实例化的时候混合进一个trait。

复制代码
trait Friendly {
  def greet() = "Hi"
}

class Dog extends Friendly {
  override def greet() = "Woof"
}

class HungryDog extends Dog {
  override def greet() = "I'd like to eat my own dog food"
}

trait ExclamatoryGreeter extends Friendly {
  override def greet() = super.greet() + "!"
}

var pet: Friendly = new Dog
println(pet.greet())

pet = new HungryDog
println(pet.greet())

pet = new Dog with ExclamatoryGreeter
println(pet.greet())

pet = new HungryDog with ExclamatoryGreeter
println(pet.greet())
复制代码

输出为:
Woof
I'd like to eat my own dog food
Woof!
I'd like to eat my own dog food!

这样做的好处是什么呢?

从前,如果想要输出在HungryDog 类的“I'd like to eat my own dog food”输出的基础上变成“I'd like to eat my own dog food!”,那么需要再增加一个子类,并重写它的方法,给输出字串上加上!,而在trait中,只需要在HungryDog 类实例化的时候“with”上一个trait,就可以达到包装的效果了。

所以,with关键字可以用来实现包装器的功能。

 

Reference:

first step to scala

转自:http://www.cnblogs.com/elaron/archive/2013/01/18/2866142.html

下面转自:http://tianxingzhe.blog.51cto.com/3390077/1697872


1类型自动匹配(模式匹配)

2函数是有值的(匿名函数是函数的常态)

递归函数需要指定返回值

3.内部类隶属于外部类的实例本身,而java内部类属于外部类,对外部类的依赖路径依赖

4.object类似于java中的静态内部类 里面的所有成员都是静态的,适用于配置文件

静态都是用来修饰类的内部成员的。比如静态方法、静态成员变量。它唯一的作用就是随着类的加载(而不是随着对象的产生)而产生,以致可以用类名+静态成员名直接获得。这样静态内部类就可以理解了,它可以直接被用 外部类名+内部类名 获得。


5.同名object是class的伴生对象,很多时候伴生对象是做为伴生类静态成员的封装区域

6.scala方法的最后一行是返回值无return

7.scala可变参数Int*  java中 int... args

8.apply使用与单例

http://book.51cto.com/art/201408/449448.htm

9.接口trait(可以有非抽象方法)比 interface (只能有抽象方法) 功能强大很多  extends ..... with....with....

跟java一样只能继承一个trait或者class,如果还要继承其他trait需要with(对象混入)

10. 包对象(package object)  包中所有类可以直接访问包对象的所有成员和方法

11scala包的隐式引用

import java.lang._

import scala._

import Predef._

12.映射符号 => lambda表达式,表示一个匿名函数

13.占位符  _  placeholder 模式匹配代表字符串

14. 本地函数 在函数中定义的函数,内部函数属于私有函数,外部不可以访问,实现高内聚低耦合

15.偏函数 _ 代表参数 编译器根据sum _ (空格+下划线)自动产生类里的apply方法 传递部分参数变成另外一种函数

16.闭包 javascript支持 闭包就是能够读取其他函数内部变量的函数,由于javascript的语言特性,如果你想读取一个函数内部的变量,那你本身也必须是这个函数内部子函数。

17.高阶函数 map() 以函数作为参数


18.模式匹配提取器 Extrator

                      match 

                      case

                      case

模式匹配在具体数据类型的上的应用


19.正则表达式后面带个.r

20.Scala的case class和pattern matching(模式匹配),这俩个两个程序结构对于处理树结构的数据非常有帮助。Scala的case class使得对对象进行模式匹配变得非常方便,简单的来说,Scala的case class就是在普通的类定义前加case这个关键字,然后你可以对这些类来模式匹配。

成员默认是val的,成员未显式声明var或者val的话,默认是val(常量)

每个case class都会有伴生对象里面有apply方法 

伴生对象会帮我们构造出case class的具体对对象


unapply从已经构建出的对象中(case class or object)提取出内容,而伴生对象的apply方法是构建对象


21 构造器的函数名为this,重载构造器(附属构造器)必须要调用其他的附属构造器,其他的构造器在最终形态上会调用主构造器,scala函数若没有参数省略小括号,this()可以简写成this

22.  别名 @符号可以引用匹配到的对象 index@"Flink"

23 sealed封闭   只能在当前文件里头被继承,限制子类必须在同一个文件中

Option 的两个子类 Some None

24.两个::表示往List头部添加元素,三个:::组合List,List集合分为head(第一个)和tail两部分

val hadoop= new Hadoop ::Nil


25.拉链操作 zip

26.flatMap  集合map后合并多个集合

27.List更多操作  partition(分区) span  find(第一个满足条件的元素) takeWhile  dropWhile 


exists (有一个满足返回true) forall (全部满足返回true)

28.foldLeft  foldRight从最左边开始和最右边开始  sortwith(_<_)排序操作

29. List.range(2,5)   半闭半开区间  List(2,3,4)不包括5

30.List.map2同时对两个集合相应元素操作

31.可变集合需要引入mutable包,默认是不可变的集合

可变集合可以直接用+=和-=追加删除元素,++=和--=追加删除集合

32.HashMap非常高效的,对key进行hashcode取值

33.treeMap,treeSet有序的

34.List[+A]协变

35. <- 提取符号(迭代器)


37. f 代表函数

38. Bounds 类型限定符号 

                                         < :  上界   只能是子类

                                         > :  下界   只能是父类

              更常用             < %  视图界定 不是子类会隐式转换  Int ->RichInt String->RichString

39.  Ordered[T]对象可以用>或者<符号比较对象代替Comparable[T]的comapreTo方法

40. [T  : Ordering] context bounds 上下文界定 存在一个类型为Ordering[T]的隐式值

   spark大量使用

41. [T : Manifest]  内部自动构建Manifest类型的隐式参数 implicit

      [T : ClassTag]  编译时会自动翻译成隐式参数和隐式值

      虚拟机运行时泛型是擦除的,不认识泛型 ,必须给具体的类型

42. 理解即可 类型约束   A < : < B  A必须是B的子类比< :更严格,编译的时候就会去判断类型

                       A=:=B  A和B是同类型  

43.    [+A] covariance(协变)  [-A] contravariance(逆变)

如果一个类型支持协变或逆变,则称这个类型为variance(翻译为可变的或变型),否则称为invariant(不可变的)

先说说协变和逆变(实际上还有非变)。协变和逆变主要是用来解决参数化类型的泛化问题。由于参数化类型的参数(参数类型)是可变的,当两个参数化类型的参数是继承关系(可泛化),那被参数化的类型是否也可以泛化呢?Java中这种情况下是不可泛化的,然而Scala提供了三个选择,即协变、逆变和非变。下 面说一下三种情况的含义,首先假设有参数化特征Queue,那它可以有如下三种定义。 
1)trait Queue[T] {} 
这是非变情况。这种情况下,当类型S是类型A的子类型,则Queue[S]不可认为是Queue[A]的子类型或父类型,这种情况是和Java一样的。 

2)trait Queue[+T] {} 
这是协变情况。这种情况下,当类型S是类型A的子类型,则Queue[S]也可以认为是Queue[A}的子类型,即Queue[S]可以泛化为Queue[A]。也就是被参数化类型的泛化方向与参数类型的方向是一致的,所以称为协变。 

3)trait Queue[-T] {} 
这是逆变情况。这种情况下,当类型S是类型A的子类型,则Queue[A]反过来可以认为是Queue[S}的子类型。也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变。



44. 默认情况下只要方法(函数)是泛型的,它的参数是逆变的,返回值就是协变的


Scala规定,协变类型只能作为方法的返回类型,而逆变类型只能作为方法的参数类型。类比函数的行为,结合Liskov替换原则,就能发现这样的规定是非常合理的。

里氏替换原则(Liskov Substitution Principle LSP)

 

      里氏替换原则是面向对象设计的基本原则之一。任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当子类可以替换基类,软件单位的功能不受影响时,基类才能真正的被复用,而子类也可以在基类的基础上增加新的行为。


        里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。

  • 子类中可以增加自己特有的方法。

  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

参考文章:

http://www.tuicool.com/articles/vaAnmq


45.this.type表示当前对象(this)的类型,this指代当前的对象。this.type被用于变量,函数参数和函数返回值的类型声明,主要是在某些场合下加强类型约束,或者说是为了确保类型的绝对安全,链式调用

46. type相当于起个别名

47.  spark很少使用 结构类型 不想从类或者接口的角度限制它, 传入的参数包含某种方法

48. 复合类型(extend ....with ....with.... )与结构类型结合使用 

49.infix type (中置类型)  中置表达式(右结合)   :> 代表函数名   中置类型:类型名称写在两个参数中间,泛型的实例在类型的两侧


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object :> {
     def unapply[A] (list: List[A]) = {
         Some( (list.init, list.last) )
     }
}
 
object Extractor_Advanced {
 
   def main(args: Array[String]) {
     ( 1  to  9 ).toList match{  case  _ :>  9  => println( "Hadoop" ) }
     ( 1  to  9 ).toList match{  case  x :>  8  :>  9  => println( "Spark" ) }
     ( 1  to  9 ).toList match{  case  :>(:>(_, 8 ), 9 ) => println( "Flink" ) }
     
   }
 
}


50.self type 自身类型 self => 给this实例指针起别名   内部类引用非常方便  this:S1 =>自身类型实例约束必须混入S1


51.鸭子模型与动态语言 不关注类型而关注方法与属性

52.type抽象类型 与 类型参数(语法比较复杂)

type In=String

53.隐式转换总结查找范围为作用域或者伴生对象

我们需要某个类中特殊方法,但是这个类没有提供这样一个方法,所以我们需要隐式转换成提供了这个方法的类,然后在调用这个方法

需要一个增强类,RichInt,RichFile

隐式转换函数 implicit def

54.隐式参数:有个隐式default值

   参数颗粒化

55.隐式类implicit class 

56.隐式对象 implicit object

57.伴生对象隐式转换不需要import 

58.并发继承Actor编程(Akka分布式并发消息驱动框架)  覆写def act(){  }

59.actor工具方法创建匿名Actor

   !发消息

    receive中代码块转换成偏函数,说他是偏函数因为返回的计算结果有很多类型

60.  主线程当作Actor使用 self.receive{}   main主线程接受其他线给主线程发送的消息

      self.receiveWithin()  指定超时时间不会一直阻塞

      sender !  给发送者返回消息

      线程之间使用case class或者class object传递消息

61. react loop 线程重用   替代while(true) { recieve { } }的方式

62.scala swing编程覆写top方法

63. listenTo(button)  deafTo(button) 监听和解除监听

事件栈

    reactions +={

   case ButtonClicked(button)=>{

    }

  }

64.for循环中的模式匹配

  for其实调用的是foreach

for((k,v:Int) <-List(("spark"->5),("Hadoop"->"Big Data"))) { println(k)}

65.模式匹配中不能用大写字母,大写字母会被认为是常量

    模式匹配编译器是lazy级别的,不会报错,运行会报错

66.getClass实例   classOf 类型本身

   classOf [ ] 更高级别   typeOf[  ]更详细级别

67.所有的对象都有一个唯一的.type的单例类型

   this.type构成了链式表达式返回的是动态当前实际运行的实例

68.List的两个子类 class  ::和object Nil(空)

69.外在的函数式和内部的可变性是非常精妙的组合

70.for表达式的生成器、定义器、过滤器

 71.play框架

72.for循环取代map,flatmap,filter

73.akka的Actor是树状结构( 分层)的共享一些公共信息

  actor有路径  akka://HelloAkka/user/master

子Actor路径

   akka://HelloAkka/user/master/map

akka://HelloAkka/user/master/reduce

74.SBT专为scala设计的构建工程  交互式命令操作  安装SBT插件

75.事件驱动的编程模型  消息邮箱

    preStart postStop preReStart 

  ActorSystem ActorRef(actor的引用)

 创建actor的过程伴随actor的启动,创建完可以直接发消息

76.Actor的构造器 默认的  传入类名   非默认的传入其他actor从而发消息

77. akka中消息的发送方式  (都是异步的)

   1.Fire and Forget  不等待对方返回结果 java:tell,scala:!

   2.Send and Receive         java:ask  scala:?   future

   主线程也属于一个actor  在/temp路径下

78.deadletters  消息接受actor死亡

   forward转发会携带原始sender

79.停止Actor

shutdown()停止ActorSystem

 master!PoisonPill  (药丸) 异步的

 master!kill  同步的

 context.stop(self) 异步的

80.monitoring  Actor

   watch  unwatch

81.动态切换actor的处理逻辑

  become unbecome 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值