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: