Scala基础(一),Scala基础(
Scala基础(一),Scala基础(
这个章节的内容包含
- 关于这个教程
- 表达式
- 值
- 变量
- 函数
- 类
- 基本继承
- traits
- 类型
关于这个教程
在最初的几个星期里,我们会讲解Scala的基本的语法和概念。然后我们会通过更多的练习来展示这些概念。有些例子是通过解释器里的几行代码来展示的,有些则是通过源码的形式进行展示。
你最好安装一个scala解释器,它可以方便你探索问题。
为什么用Scala?
- 富有表现力
- 一等函数
- 闭包
- 简洁
- 类型接口
- 支持函数创建的语法
- 可以和Java进行互操作
- 可以复用Java的类库
- 可以复用Java的工具
- 没有额外的性能问题
Scala 怎么运行?
- Scala代码会编译成Java 字节码
- 可以很好的运行在任何标准的JVM上
- 甚至可以运行在非标准的JVM上,例如Dalvik
- Scala的编译器是由Java编译器的作者编写的
按照Scala的方式思考
Scala 不仅仅是Java的改善。你需要用一个全新的思维方式来学习它,你会从这些课程中学到更多。
启动解释器
启动内置的sbt console
$ sbt conole [...] Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20). Type in expressions to have them evaluated. Type :help for more information. scala>
表达式
scala> 1 + 1 res0: Int = 2
res0是由解释器自动给你的表达式的结果生成的变量名。它的类型是Int,值是2。 在Scala里(几乎)所有的东西都是一个表达式。
值
你可以给表达式的结果设定一个名字。
scala> val two = 1 + 1 two: Int = 2
你不可以改变val变量的绑定。
变量
如果你需要改变绑定,你可以使用var
来代替。
scala> var name = "steve" name: java.lang.String = steve scala> name = "marius" name: java.lang.String = marius
函数
你可以用def来创建函数
scala> def addOne(m: Int): Int = m + 1 addOne: (m: Int)Int
在Scala里,你需要给函数的参数指定类型签名。解释器会回显你指定的类型签名。
scala> val three = addOne(2) three: Int = 3
对于没有参数的函数,在调用的时候可以省略掉括号
scala> def three() = 1 + 2 three: ()Int scala> three() res2: Int = 3 scala> three res3: Int = 3
匿名函数
你可以通过下面的方式创建匿名函数
scala> (x: Int) => x + 1 res2: (Int) => Int =
这个函数给一个Int型的整数加一。
scala> res2(1) res3: Int = 2
你可以把匿名函数作为参数进行传递,或者把它们保存在val中。
scala> val addOne = (x: Int) => x + 1 addOne: (Int) => Int = scala> addOne(1) res4: Int = 2
如果你的函数是由多个表达式组成的,你可以把它们写在一对{}里。
def timesTwo(i: Int): Int = { println("hello world") i * 2 }
也可以通过下面的方式来创建一个匿名函数
scala> { i: Int => println("hello world") i * 2 } res0: (Int) => Int =
在把匿名函数作为一个参数进行传递时,你会经常看到这种语法。
部分应用(偏函数)
你可以使用下划线来部分应用一个函数,这样就可以产生一个新的函数。Scala用下划线来表示在不同上下文下的不同内容,不过你可以认为它是一个没有名字的神奇通配符。在{ _ + 2}
这样的上下文里,它表示一个没有名字的参数。你也可以这样来使用它:
scala> def adder(m: Int, n: Int) = m + n adder: (m: Int,n: Int)Int
scala> val add2 = adder(2, _:Int) add2: (Int) => Int = scala> add2(3) res50: Int = 5
你可以部分应用参数列表里的任何参数,而并非只是最后一个。
Curried functions
有时候,使用者会先给你的函数传入部分参数,后面再传入其他的参数,这样做是很有用的。
下面的例子中,你可以对两个数进行乘法运算。在第一次调用时,你可以确定哪个是乘数,在第二次调用时,再确定被乘数。
scala> def multiply(m: Int)(n: Int): Int = m * n multiply: (m: Int)(n: Int)Int
你可以直接把两个参数都传入。
scala> multiply(2)(3) res0: Int = 6
或者,你也可以先传入第一个参数,然后部分应用第二个参数。
scala> val timesTwo = multiply(2) _ timesTwo: (Int) => Int = scala> timesTwo(3) res1: Int = 6
你可以把任何有多个参数的函数当作curried function来使用。我们来用之前的adder
做个示范。
scala> (adder _).curried res1: (Int) => (Int) => Int =
可变长度的参数
对于方法,还有一个特别的语法用来接收多个相同类型的参数。对于一组字符串,要对它们应用String的capitalize
函数,你可以这样写:
def capitalizeAll(args: String*) = { args.map { arg => arg.capitalize } } scala> capitalizeAll("rarity", "applejack") res2: Seq[String] = ArrayBuffer(Rarity, Applejack)
类
scala> class Calculator { | val brand: String = "HP" | def add(m: Int, n: Int): Int = m + n | } defined class Calculator scala> val calc = new Calculator calc: Calculator = Calculator@e75a11 scala> calc.add(1, 2) res1: Int = 3 scala> calc.brand res2: String = "HP"
上面的例子中,展示了通过def定义方法以及使用val定义成员变量。方法也是函数,只不过它可以访问类的状态。
Constructor
Constructor并不是特殊的方法,它们是当前类外部的一段代码。我们来扩展一下Calculator的例子,使得它能够接受一个参数,并且使用这个参数来初始化类的内部状态。
class Calculator(brand: String) { /* * A constructor. */ val color: String = if (brand == "TI") { "blue" } else if (brand == "HP") { "black" } else { "white" } // An instance method. def add(m: Int, n: Int): Int = m + n }
注意两种不同方式的注释。
你可以使用这个constructor来构造一个实例:
scala> val calc = new Calculator("HP") calc: Calculator = Calculator@1e64cc4d scala> calc.color res0: String = black
表达式
这个BasicCalculator例子向我们展示了Scala是如何面向表达式的。变量color的值取决于一个if/else表达式的结果。Scala是高度面向表达式的:大部分的语句都是表达式,而非声明。
题外话:函数 vs 方法
函数和方法在很大的程度上是可以相互替代的。因为函数和方法是如此的相似,以至于你可能无法分清你调用的对象是一个方法还是一个函数。当你考虑函数和方法的区别时,你就开始混乱了。
scala> class C { | var acc = 0 | def minc = { acc += 1 } | val finc = { () => acc += 1 } | } defined class C scala> val c = new C c: C = C@1af1bd6 scala> c.minc // calls c.minc() scala> c.finc // returns the function as a value: res2: () => Unit =
当你调用一个“函数”但是没有带上括号但实际上没效果时,你也许会想噢,我想我明白Scala的函数是怎么起作用的,但是我认为也许不是这样的。或许有时候是需要括号的?或许你对函数比较了解,但是实际上却在使用一个方法。
实际上,就算你在区别函数和方法的问题上犯迷糊,你还是可以用Scala来做一些很出色的事情。如果你是一个Scala新手,并且你在google上阅读它们的区别,你可能会不是很明白。但是这并不表示你会用不好Scala。它只能说明函数和方法的区别是比较微妙的,如果要解释的话,那么就需要对语言进行深入了解。
基本继承
class ScientificCalculator(brand: String) extends Calculator(brand) { def log(m: Double, base: Double) = math.log(m) / math.log(base) }
参考 《Effective Scala》中指出,如果子类和父类并没有什么大的区别的话,类型别名(Type alias)比extends
更好。《A Tour of Scala》中讲述了子类化(Subclassing)的技术。
重载方法
class EvenMoreScientificCalculator(brand: String) extends ScientificCalculator(brand) { def log(m: Int): Double = log(m, math.exp(1)) }
抽象类
你可以定义一个抽象类,抽象类可以定义一些方法,但是不需要实现它们。相反,继承抽象类的子类需要实现这些方法。抽象类是不能被实例化的。
scala> abstract class Shape { | def getArea():Int // subclass should define this | } defined class Shape scala> class Circle(r: Int) extends Shape { | def getArea():Int = { r * r * 3 } | } defined class Circle scala> val s = new Shape :8: error: class Shape is abstract; cannot be instantiated val s = new Shape ^ scala> val c = new Circle(2) c: Circle = Circle@65c0035b
Traits
traints
表示一系列可以扩展或者混入到你的类里的成员和行为。
trait Car { val brand: String } trait Shiny { val shineRefraction: Int }
class BMW extends Car { val brand = "BMW" }
通过with
关键字,一个类可以扩展多个traint:
class BMW extends Car with Shiny { val brand = "BMW" val shineRefraction = 12 }
参考 《Effective Scala》中关于 trait的观点.
什么时候你需要使用Trait代替抽象类? 如果你想定义一个类似接口的类型,那么你很难在trait和抽象类之间做出选择。它们都可以让你定义一个具有某些行为的类型,然后要求扩展者去实习其他的行为。下面是一些经验法则:
- 优先使用traint。一个类可以扩展多个traint,但是只能扩展一个抽象类。
- 如果你需要在构造类的时候传入参数的话,那就是用抽象类。抽象类的构造器可以传入参数,trait则不能。例如,你不能这样写
trait t(i:Int) {}
;参数i
是非法的。
你并不是第一个问这个问题的人。你可以在《Scala traits vs abstract classes》, 《Difference between Abstract Class and Trait》, 和《Programming in Scala: To trait, or not to trait?》查看更加完整的解释。
类型
在前面的例子里,你见过我们定义了一个函数,它接收一个Int
参数,而Int的类型是Number。函数也可以是泛型的,并且可以作用于任何类型。当你遇到这种情况时,你会看到类型参数是放在方括号里的。下面是一个支持泛型键-值的Cache示例。
trait Cache[K, V] { def get(key: K): V def put(key: K, value: V) def delete(key: K) }
方法也可以引入类型参数。
def remove[K](key: K)
英文原文:Scala School,翻译:Wld5 - 朱伟杰
译文原文:http://www.wld5.com/3240.html
【如需转载,请在正文中标注并保留原文链接、译文链接和译者等信息,谢谢合作!】
用户点评