作者Schelfaniel (Schelfaniel)
看板PLT
标题[闲聊] Scala 超短介绍(续1)
时间Mon Aug 17 09:45:07 2009
所以,我们可以知道,Scala 出来并不是要取代掉 Java,它是要和 Java 共存,
提供程式设计师另一个选择。其实,只要是 Java 的元件,
Scala 都可以拿来使用,但是反过来 Scala 的元件,要写成 Java 可用,
就可能要少一些东西了,因为 Scala 的元件复杂度还是高於 Java 的。
我们拿 using 来看看
def using[T <: { def close() }]
(resource: T)
(block: T => Unit) {
try {
block(resource)
} finally {
if (resource != null) resource.close()
}
}
def 是定义函式,它的格式是
def 函式名称(函数传入值 : 型别) : 传回值型别 = { 内容 }
但是传回值型别,其实有时候 编译器 可以判断时可以省略的
而後面的 = 在没有传回值型别时可以省略,所以就变成
( 注:函数语言之中,凡函数一定会传回值,如果没有传回值,
则会传回一个定值 Unit,
虽然它还是有传回值,但是我们这边就暂时把它当成无传回值 )
def 函式名称(函数传入值 : 型别) { 内容 }
但是这边是
def 函式名称[样板] (函数传入值:型别)(函数传入值:型别) { 内容 }
因为 Scala 支援样板(template),所以可以使用 Template,
这个样板等於说是,我们不明确指定要什麽型别,让 编译器 於需要时自己生出来
一般写 [T] 就表示 T 为样板,但是这边是用 [T <: {def close() }]
<: 是样板用的继承,它不是真的像类别的继承会生出一个新类别,
而是表明样板必须要是这样类别的子类别。
为什麽不用 <> 来表示样板呢?
因为 Scala 中,有内建 XML 型别的 val a = <test>test</test> 这是可以过的,
所以它样板全部改用 [] 来定义,而原本阵列等使用 [] 的,
则变为和函数用的 () 一样。
而我们这边用 {def close() },来表示 Duck Typing,
这原本在动态语言中比较常见,是说只要有这个功能就好了,不管它是怎麽来,
可以避免到继承树太过复杂错乱的情形,而这边 Scala 也有支援。
眼细的人可能会注意到 (函数传入值:型别)(函数传入值:型别) 这样两个定义,
它不是写 (函数传入值:型别, 函数传入值:型别) 一般传入的处理,
因为它处理上不是平行的,也就是说,它在第一个函数传入值处理完之後,
会先产生出部份的片段,然後再把第二段代入,
像是 using 时,它的两个函数传入为:
(resource: T)(block: T => Unit)
第一个是资源本身,型别不定,所以设为 T
而第二个是程式区块,型别是 T => Unit ( 输入 T ,而输出 Unit 表示不输出 )
它先产生出资源本身,再把资源代入第二段,所以算是分次处理。
所以这样 using 定义完了之後,接下来使用起来就比原本顺手多了。
从此例可见 Scala 也具有强大的 DSL 制定能力,
在 Lift 网页架构之中,其行数可以和 RoR 相敌,却又保持型别检查,
这在语言之中是很少见的。
接下来我们看一下 Scala 少了什麽?
Java 有, Scala 没有的:
●static 静态
●primitive types 基础型别
●break continue 回圈控制(但 2.8 版要加回 break)
●interface 介面
●wildcard
●raw type
●enum 列举类别
●static 静态 -> Scala 使用 object 取代
像 Java class 如下
public class A {
private static int count = 0;
public static int getCount() { return count; }
public A () { count++; }
}
Scala 的写法是 :
object A {
var count = 0
def getCount = count
}
class A {
A.count += 1
}
Scala 把 A 拆成两项,一个 object 一个 class。
( 注: object 和 class 名称不一定要相同,但是这边是从 Java 的例子拆出来的,
所以先使用相同的 )。
注意的是这边 Scala 预设下是 public,
而 object 中的 count 如果设 private 会让 class A 也读不到,
这边正常要写 private[A],可是这个 private Scope 比较复杂先暂略,
直接设成 public。
其实,在对物件导向的初学者讲解 static 时,常常会有人弄不懂,
不管是说 static 是属於 class 的,而非属於 instance 的,
或是说 static 时不用传入 this,但 instance 时需要传入 this。
而这边 Scala 就乾脆拆成两个定义,类别的放 object,实例的放 class,
所以我们可以看到静态的有一个 count 初值是 0,这边型别我们省略会自动放 Int
( 注: Scala 没有特别区分 int 或 Integer,直接用 Int,但特殊情形之下可以再区别
)
而前面我们使用 var 表示这是会变的,如果不会变的写 val ( 类似 Java 的 final )。
而第二行的 def getCount 表示它是一个函式,而它的内容就直接 count,
我们不用写 def getCount : Int = { return count } 这样,
可以直接省略成 def getCount = count
其次是 class A 的部份,咦??建构子呢??
其实 Scala 中,直接包在 class 的部份就算是建构子了,
这边我们连建构子的宣告定义都省了,就直接把内容包在 class 中,
看起来也清爽多了吧。
而这边必须写 A.count += 1,一来是我们必须表明是 object A 中的 count,
它不是自己的 count,另一来 Scala 中没有 ++,
所以必须使用 += 1 来处理。
●primitive types 基础型别
Scala 中强调 Everything is object,
所以一般情形之下,不会使用基础型别,
它的型别 Byte Short Int Long Char Float Double
都是物件型别。
●break continue 回圈控制(但 2.8 版要加回 break)
这是因为函数语言的特性,Scala 决定把这两个取消,
对於非函数语言式写法有时需要调整一下,
不过 2.8 把 break 加回来,也许哪一天 continue 也会加回来。
●interface 介面 -> Scala 使用 trait 取代
这 trait 比 interface 复杂很多,下回分解
●wildcard
Scala 中没有 List<?> 这样的,取而代之的是 List<Any>
其中 Any 也是一个物件类别。
●raw type
Scala 中没有不包含 generic 的 List 它会变成 List[Nothing],
这通常不是我们要的,如果要和 Java 介接时,要以 List[_] 来处理。
另外 Scala 的阵列和 Java 有点不同。
Java:
int[] a = new int[10];
Scala:
var a : Array[Int] = new Array[Int](10)
型态可省略
var a = new Array[Int](10)
●enum 列举类别
Scala 使用的是 Enumeration 物件
使用时必须要自订 class extends Enumeration
由此可见,大部份 "Java 有,而 Scala 没有" 其实问题都不大,
大多都是 Scala 为了让程式更加物件化,
或是更偏函数性(去掉 break continue) 等等来做的修改。
一旦这些Scala从Java去掉的部份能够适应,
Java 程式师要转为 Scala 程式师,并不是很大的障碍,
这障碍其实感觉比 C 转到 C++ 还来得小。
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 163.29.29.137
※ 编辑: Schelfaniel 来自: 163.29.29.137 (08/17 15:48)