Scala学习笔记(1)

为什么选择Scala?

  • 表达能力

    • 函数是一等公民
    • 闭包
  • 简洁
    • 类型推断
    • 函数创建的文法支持
  • Java
    • 可重用java库
    • 可重用java工具
    • 没有性能惩罚

Scala如何工作?

  • 编译成Java字节码
  • 可在任何标准JVM上运行
    • 甚至是在一些不规范的JVM上,如Dalvik
    • Scala编译器是Java编译器的作者写的

下载和安装

可以在http://www.scala-lang.org/上下载最新的scala安装包。下载后解压,然后切换到scala所在目录,执行:

  1. $ cd /export/scala/scala-2.11.4 
  2. $ bin/scala
  3. Picked up _JAVA_OPTIONS:   -Dawt.useSystemAAFontSettings=gasp
  4. Welcome to Scala version 2.11.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_45).
  5. Type in expressions to have them evaluated.
  6. Type :help for more information.
  7. scala>

表达式和值

在Scala中,几乎所有的语言元素都是表达式。

  1. scala> println("hello wolrd")
  2. hello wolrd

是一个表达式,

  1. scala> "hello"+" world"
  2. res2: String = hello world

也是一个表达式。

可以通过val定义一个常量,亦可以通过var定义一个变量。推荐多使用常量。

函数是一等公民

可以使用def来定义一个函数。函数体是一个表达式。

使用Block表达式的时候,默认最后一行的返回是返回值,无需显式指定。

函数还可以像值一样,赋值给var或val。因此,函数也可以作为参数传给另一个函数。

  1. def square(a: Int) = a * a
  2. def squareWithBlock(a: Int) = {
  3.     a * a
  4. }
  5. val squareVal = (a: Int) => a * a
  6. def addOne(f: Int => Int, arg: Int) = f(arg) + 1
  7. println("square(2):" + square(2))
  8. println("squareWithBlock(2)" + squareWithBlock(2))
  9. println("squareVal(2):" + squareVal(2))
  10. println("addOne(squareVal,2)" + addOne(squareVal,2))

如果函数不带参数,你可以不写括号。

  1. scala> def three() = 1 + 2
  2. three: ()Int
  3. scala> three()
  4. res3: Int = 3
  5. scala> three
  6. res4: Int = 3

匿名函数

你可以创建匿名函数

  1. scala> (x: Int)=>x+1
  2. res5: Int => Int = <function1>
  3. scala> res5(4)
  4. res6: Int = 5

你可以传递匿名函数,或将其保存成不变量。

  1. scala> val addOne = (x: Int)=>x+1
  2. addOne: Int => Int = <function1>
  3. scala> addOne(3)
  4. res7: Int = 4

部分应用

你可以使用下划线"_"部分应用一个函数,结果将得到另一个函数。Scala使用下划线表示不同上下文中的不同事物,你通常可以把它看做是一个没有命名的神奇通配符。在( _ + 2)的上下文钟,它代表一个匿名参数。你可以这样使用它:

  1. scala> def adder(m: Int, n: Int) = m + n
  2. adder: (m: Int, n: Int)Int
  3. scala> val add2 = adder(2, _:Int)
  4. add2: Int => Int = <function1>
  5. scala> add2(3)
  6. res8: Int = 5

你可以部分应用参数列表中的任意参数,而不仅仅是最后一个。

柯里化函数

有时候会有这样的需求:允许别人一会在你的函数上应用一些参数,然后又应用另外的一些参数。

例如一个乘法函数,在一个场景需要选择乘数,而另一个场景需要选择被乘数。

  1. scala> def multiply(m: Int)(n: Int) : Int = m*n
  2. multiply: (m: Int)(n: Int)Int

你可以直接传入两个参数。

  1. scala> multiply(2)(3)
  2. res9: Int = 6

也可以填上第一个参数并且部分应用第二个参数。

  1. scala> val timesTwo = multiply(2) _
  2. timesTwo: Int => Int = <function1>
  3. scala> timesTwo(3)
  4. res10: Int = 6

可变长度参数

这是一个特殊的语法,可以向方法传入任意多个同类型的参数。例如要在多个字符串上执行String的capitalize函数,可以这样写:

  1. scala> def capitalizeAll(args: String*) = {
  2.      |   args.map { arg =>
  3.      |     arg.capitalize
  4.      |   }
  5.      | }
  6. capitalizeAll: (args: String*)Seq[String]
  7. scala> capitalizeAll("rarity", "applejack")
  8. res11: Seq[String] = ArrayBuffer(Rarity, Applejack)

借贷模式

由于函数可以像值一样作为参数传递,所以可以方便的实现借贷模式。

下面的例子从/proc/self/stat文件中读取当前进程的pid。

withScannerf封装了try-finally块,所以调用者不用再close。

注意:当表达式没有返回值的时候,默认返回Unit。

  1. import scala.reflect.io.File
  2. import java.util.Scanner
  3. def withScanner(f: File, op: Scanner => Unit) = {
  4.      val scanner = new Scanner(f.bufferedReader)
  5.      try {
  6.          op(scanner)
  7.      } finally {
  8.           scanner.close()
  9.      }
  10. }
  11. withScanner(File("/proc/self/stat"),scanner => println("pid is" + scanner.next()))

按名称传递参数

下面的例子演示按照名称传递参数,由于有除以0,所以运行该程序会长生异常。

  1. scala> var logEnable = false
  2. logEnable: Boolean = false
  3. scala> def log(msg: String) = 
  4.      | if (logEnable) println(msg)
  5. log: (msg: String)Unit
  6. scala> val MSG = "programming is running"
  7. MSG: String = programming is running
  8. scala> log(MSG + 1/0)
  9. java.lang.ArithmeticException: / by zero
  10.   ... 33 elided

将def log(msg: String)修改成def log(msg: => String),由按值传递改为按名称传递后将不会产生异常。

因为log函数的参数是按名称传递,参数会等到实际使用的时候才会计算,所以被跳过。

  1. scala> def log(msg: => String) =
  2.      | if (logEnable) println(msg)
  3. log: (msg: => String)Unit
  4. scala> log(MSG + 1/0)

按名称传递参数可以减少不必要的计算和异常。

定义类

可以用class关键字来定义类。并通过new来创建类。

在定义类时可以定义字段,如firstName,lastName。这样做还可以自动生成构造函数。

可以在类中通过def定义函数。var和val定义字段。

函数名是任何字符如+,-,*,/。

试着将

obama.age_=(51)

简化为

obama.age = 51

这样的简化更像调用一个变量。

  1. scala> class Person(val firstName: String, val lastName: String) {
  2.      | private var _age = 0
  3.      | def age = _age
  4.      | def age_=(newAge: Int) = _age = newAge
  5.      | 
  6.      | def fullName() = firstName + " " + lastName
  7.      | 
  8.      | override def toString() = fullName()
  9.      | }
  10. defined class Person
  11. scala> val obama: Person = new Person("Barack", "obama")
  12. obama: Person = Barack obama
  13. scala> println("Person" + obama)
  14. PersonBarack obama
  15. scala> println("firstName:" + obama.firstName)
  16. firstName:Barack
  17. scala> println("lastName:" + obama.lastName)
  18. lastName:obama
  19. scala> obama.age_=(51)
  20. scala> println("age:" + obama.age)
  21. age:51

旁白: 函数 vs 方法

函数和方法在很大程度上是可以互换的。由于函数和方法是如此的相似,你可能都不知道你调用的东西是一个函数还是一个方法。而当真正碰到的方法和函数之间的差异的时候,你可能会感到困惑。

  1. scala> class C {
  2.      |   var acc = 0
  3.      |   def minc = { acc += 1 }
  4.      |   val finc = { () => acc += 1 }
  5.      | }
  6. defined class C
  7. scala> val c = new C
  8. c: C = [email protected]
  9. scala> c.minc // calls c.minc()
  10. scala> c.finc // returns the function as a value:
  11. res2: () => Unit = <function0>

当你可以调用一个不带括号的“函数”,但是对另一个却必须加上括号的时候,你可能会想哎呀,我还以为自己知道Scala是怎么工作的呢。也许他们有时需要括号?你可能以为自己用的是函数,但实际使用的是方法。

在实践中,即使不理解方法和函数上的区别,你也可以用Scala做伟大的事情。如果你是Scala新手,而且在读两者的差异解释,你可能会跟不上。不过这并不意味着你在使用Scala上有麻烦。它只是意味着函数和方法之间的差异是很微妙的,只有深入语言内部才能清楚理解它。

继承

  1. class SuperMan(brand: String) extends Person(brand) {
  2.   def fly() = "fly"
  3. }

参考 Effective Scala 指出如果子类与父类实际上没有区别,类型别名是优于继承的。A Tour of Scala 详细介绍了子类化。

重载方法

  1. class SuperMan(brand: String) extends Person(brand) { 
  2.   def fullName() = firstName + " " + lastName
  3. }

抽象类

你可以定义一个抽象类,它定义了一些方法但没有实现它们。取而代之是由扩展抽象类的子类定义这些方法。你不能创建抽象类的实例。

  1. scala> abstract class Shape {
  2.      |   def getArea():Int    // subclass should define this
  3.      | }
  4. defined class Shape
  5. scala> class Circle(r: Int) extends Shape {
  6.      |   def getArea():Int = { r * r * 3 }
  7.      | }
  8. defined class Circle
  9. scala> val s = new Shape
  10. <console>:8: error: class Shape is abstract; cannot be instantiated
  11.        val s = new Shape
  12.                ^
  13. scala> val c = new Circle(2)
  14. c: Circle = [email protected]

特质(Traits)

特质是一些字段和行为的集合,可以扩展或混入(mixin)你的类中。

  1. trait Car {
  2.   val brand: String
  3. }
  4. trait Shiny {
  5.   val shineRefraction: Int
  6. }
  7. class BMW extends Car {
  8.   val brand = "BMW"
  9. }

通过with关键字,一个类可以扩展多个特质:

  1. class BMW extends Car with Shiny {
  2.   val brand = "BMW"
  3.   val shineRefraction = 12
  4. }

参考 Effective Scala 对特质的观点。

什么时候应该使用特质而不是抽象类? 如果你想定义一个类似接口的类型,你可能会在特质和抽象类之间难以取舍。这两种形式都可以让你定义一个类型的一些行为,并要求继承者定义一些其他行为。一些经验法则:

  • 优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
  • 如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。例如,你不能说trait t(i: Int) {},参数i是非法的。

你不是问这个问题的第一人。可以查看更全面的答案: stackoverflow: Scala特质 vs 抽象类 , 抽象类和特质的区别,
and Scala编程: 用特质,还是不用特质?

类型

此前,我们定义了一个函数的参数为Int,表示输入是一个数字类型。其实函数也可以是泛型的,来适用于所有类型。当这种情况发生时,你会看到用方括号语法引入的类型参数。下面的例子展示了一个使用泛型键和值的缓存。

  1. trait Cache[K, V] {
  2.   def get(key: K): V
  3.   def put(key: K, value: V)
  4.   def delete(key: K)
  5. }

方法也可以引入类型参数。

  1. def remove[K](key: K)

转载请注明出处:http://blog.csdn.net/iAm333

时间: 2024-10-13 12:49:42

Scala学习笔记(1)的相关文章

Scala学习笔记及与Java不同之处总结-从Java开发者角度

Scala与Java具有很多相似之处,但又有很多不同.这里主要从一个Java开发者的角度,总结在使用Scala的过程中所面临的一些思维转变. 这里仅仅是总结了部分两种语言在开发过程中的不同,以后会陆续更新一些切换后在开发过程中值得注意的地方.以下列举了部分,但令人印象深刻的Scala语言的不同之处,具体的代码演示样例及具体阐述见下文. ? Scala中可直接调用Java代码,与Java无缝连接. 语句能够不用";"结束.且推荐不适用";". 变量声明时以var或va

原创:Scala学习笔记(不断更新)

Scala是一种函数式语言和面向对象语言结合的新语言,本笔记中就零散记下学习scala的一些心得,主要侧重函数式编程方面. 1. 以递归为核心控制结构. 实现循环处理的方式有三种:goto,for/while,递归,其中用goto实现循环已经在现代语言中被放弃,而for/while形式的结构化编程成为主流,而递归作为另一种方案,则长期只流行在函数式编程的小圈子中. 递归被主流编程界所担心的主要是过深的调用栈,甚至以前的课堂上我们还亲自尝试过将递归改写为循环,但是现代函数式编程语言中,通过尾递归(

Scala学习笔记及与Java不同之处总结-从Java开发人员角度

Scala与Java具有许多相似之处,但又有许多不同.这里主要从一个Java开发人员的角度,总结在使用Scala的过程中所面临的一些思维转变.这里只是总结了部分两种语言在开发过程中的不同,以后会陆续更新一些切换后在开发过程中值得注意的地方.下面列举了部分,但令人印象深刻的Scala语言的不同之处,具体的代码示例及详细阐述见下文. ? Scala中可直接调用Java代码,与Java无缝连接: 语句可以不用";"结束,且推荐不适用";": 变量声明时以var或val开头

Scala学习笔记一之基础语法,条件控制,循环控制,函数,数组,集合

前言:Scala的安装教程:http://www.cnblogs.com/biehongli/p/8065679.html 1:Scala之基础语法学习笔记: 1:声明val变量:可以使用val来声明变量,用来存放表达式的计算结果,但是常量声明后是无法改变它的值的,建议使用val来声明常量: 声明var变量:如果要声明可以改变的引用,可以使用var变量,声明的常量的值可以改变. 3:指定类型:无论声明val变量还是声明var变量.都可以手动指定其类型,如果不指定,scala会自动根据值,进行类型

Scala学习笔记-环境搭建以及简单语法

关于环境的搭建,去官网下载JDK8和Scala的IDE就可以了,Scala的IDE是基于Eclipse的. 下面直接上代码: 这是项目目录: A是scala写的: package first import scala.collection.mutable.ListBuffer object A { def main(args: Array[String]) { print("Hello,Scala");//学习程序设计的第一句 println("---");//pr

scala 学习笔记(04) OOP(上)

一.主从构造器 java中构造函数没有主.从之分,只有构造器重载,但在scala中,每个类都有一个主构造器,在定义class时,如果啥也没写,默认有一个xxx()的主构造器 class Person { var name: String = _ /** * 从构造器 * @param name */ def this(name: String) = { this //注意:从构造器,必须先调用主构造器 this.name = name; } override def toString = { "

Scala并发编程实战初体验及其在Spark源码中的应用解析之Scala学习笔记-56

package com.leegh.actor import scala.actors.Actor /** * @author Guohui Li */object First_Actor extends Actor { def act() { for (i <- 1 to 10) { println("Step : " + i) println(Thread.currentThread().getName) Thread.sleep(2000) } }} object Seco

Scala中复合类型实战详解之Scala学习笔记-44

package com.leegh.parameterization import com.leegh.parameterization.Compound_Type /** * @author Guohui Li */ trait Compound_Type1;trait Compound_Type2;class Compound_Type extends Compound_Type1 with Compound_Type2object Compound_Type { def compound_

Scala学习笔记-11

package com.leegh.oop import scala.io.Source /** * @author Guohui Li */object FunctionOps { def main(args: Array[String]) { val width = args(0).toInt for (arg <- args.drop(1)) processData(arg, width) var increase = (x: Int) => x + 1 increase(10) inc

Scala 学习笔记(2)之类和对象

Scala 的类大抵和 Java 是类似的,简单的例子如下: class MyClass { var myField : Int = 0; def this(value : Int) = { this(); this.myField = value; } def getMyField() : Int = { return this.myField; } def addToMyField(value : Int) { this.myField += value; } } 如果好奇,可以把编译后的代