仔细的观察这个例子,我们还可以得到Scala中数组的语法,以及如何显式的指明一个变量的类型.让我们仔细研究一下声明main方法的这行代码:
Java代码
| def main(args:Array[String]) = { def main(args:Array[String]) = { |
这儿,args是一个Array[String]类型的方法参数.也就是说,args是一个String数组.在Scala中,Array是一个具有类型参数指明其元素类型的类(一个真正的类,而不是JAVA中那样).与之对等的JAVA语法应该类似与下面这样子(假设Java中也有一个Array类):
Java代码
| public static void main(Array args) public static void main(Array args) |
在Scala中,使用variable:Type的语法来指明变量类型.因此,如果我们想显式声明一个Int类型的变量,应该通过如下方式
Java代码
| var myInteger:Int var myInteger:Int |
在例子中,我们实际上声明了一个String类型的变量greeting.然而,得益于Scala中的类型推断机制,我们并没有显式指明变量的类型.下面两种方式在语义上是等价的:
Java代码
| var greeting ="" var greeting:String ="" var greeting ="" var greeting:String ="" |
在第一种方式中,我们显然知道greeting是一个String,Scala编译器也能够推断出这一点.这两个变量都是静态类型检查的,只不过第一种方式可以少写7个字符:-)
细心的读者可能还注意到了,例子中的main方法并没有指明返回类型.这是因为Scala同样可以推断出来.如果我们想显式的指明这点,可以像下面这样:
Java代码
| def main(args:Array[String]):Unit = { def main(args:Array[String]):Unit = { |
如前面一样,类型在方法名之后,并用冒号隔开.顺便提一下,Unit是Scala中的一个类型,它表示"我不关心返回的究竟是什么东东"(原文:I-really-don't-care-what-I-return),你可以把它想象为JAVA中void类型与对象的混合物(译者注:但Unit不是void,而是一个实实在在的类,也就是说Unit类型的方法会返回一个Unit对象).
数组的遍历:
Java代码
| var greeting ="" for(i <- 0 until args.length) { greeting += (args(i) + " ") } var greeting ="" for(i <- 0 until args.length) { greeting += (args(i) + " ") } |
(译者注: 目前Scala中这种方式遍历的性能相对低下,速度大概是JAVA中对应for的1/60.这个其实也不会造成太大的影响,因为耗时一般不在于for循环本身,而是循环体内的操作.更何况在Scala中用for的机会不多,有更多简单实用的方式去实现同样的操作)
这段代码比刚才那段稍微复杂点.我们一开始声明了一个String类型的变量greeting,然后使用了一个在Scala中不常用的for循环,这里有一些隐含的类型推断.有Ruby经验的知道在Ruby中的等价形式:
Ruby代码
| for i in 0..(args.size - 1) greeting += args[i] + " " end for i in 0..(args.size - 1) greeting += args[i] + " " end |
问题的关键在于,Scala的for循环语句需要一个Range对象,而这个Range对象是由RichInt的until方法建立的.我们可以把上面的代码分开写:
Java代码
| val range =0.until(args.length) for(i <- range) { val range =0.until(args.length) for(i <- range) { |
注意,这里使用"val"而不是"var"声明变量.使用val,就如java中final的语义,指明range是一个常量.
在Scala中,我们可以通过多种不同的形式调用方法.在这个例子中,我们看到了"value methodName param"与"value.methodName(param)"这两种形式的语法是等价的.另外一个很重要的事情是,until方法是RichInt具有的,Int并没有该方法.这里存在一个隐式类型转换,将Int类型的0转换为scala.runtime.RichInt的一个实例.这里不去深究这个类型转换是怎样进行的,我们关心的是RichInt类确实有一个返回Rangle对象的until方法.
因此,事实上这段代码与JAVA中的下列代码在逻辑上是等价的:
Java代码
| for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) { |
只不过在JAVA中是显式的遍历整个区间,而Scala中使用了一个Range对象.
可能你还注意到了,循环体中访问数组args元素使用的是圆括号"()",而不是我们习惯的方括号"[]".
更好的遍历方式:
在JAVA 5中引入了一个foreach的语法,像下面这样:
Java代码
| for(String arg: args) { greeting += arg + " "; } for(String arg: args) { greeting += arg + " "; } |
这样更简洁明了.Scala中使用高阶函数(使用函数做参数的函数)实现类似的功能:
Ruby代码
| args.foreach {arg => greeting += (arg + " ") } args.foreach {arg => greeting += (arg + " ") } |
我们可以看到,Array类有一个foreach方法,这个方法将一个函数作为参数.foreach方法将会对Array里的每一个元素调用一次这个函数,并在调用的时候将这个元素作为函数参数传递给函数.这里参数arg没有指明类型,但由于我们是对一个String数组遍历,编译器能推断出它的类型是String.我们已经提到过,Scala中调用方法有多钟不同的方式.这里我们就省略了圆括号"()",同样我们也可以写成:
Ruby代码
| args.foreach(arg => { greeting += (arg + " ") }) args.foreach(arg => { greeting += (arg + " ") }) |
Scala中我们可以使用更加简洁的书写方式:
Ruby代码
| args.foreach(arg => greeting +=(arg + " ")) args.foreach(arg => greeting +=(arg + " ")) |
不错吧,现在我们将整个例子重写成:
Ruby代码
| object HelloWorld2 { def main(args:Array[String]) = { var greeting = "" args.foreach(arg => greeting += (arg + " ")) if (args.length > 0) greeting = greeting.substring(0, greeting.length - 1) println(greeting) } } object HelloWorld2 { def main(args:Array[String]) = { var greeting = "" args.foreach(arg => greeting += (arg + " ")) if (args.length > 0) greeting = greeting.substring(0, greeting.length - 1) println(greeting) } } |
Scala的内置类型:
因为Scala是基于JVM的,因此它从JAVA那儿继承了大量的API.这意味这你能够与JAVA交互使用.并且,你所写的Scala代码事实上使用的就是Java API.比如上面的HelloWorld2中我们使用了一个字符串变量greeting,这个变量事实上就是Java中的String类型.再比如,当你在Scala中声明一个整数类型(Int),编译器会自动将它转换为Java的基本类型int.
Scala还内置了一些隐式类型转换,当需要的时候编译器会自动进行(就像例子中的Int到RichInt类型的转换).比如在Scala中将Array[String]类型传递给需要String[]类型参数的函数时,隐式类型转换就会发生.甚至Scala中的类型参数可以和Java中的泛型相互转化.简单的说,Scala就是一个披着不同语法外衣的Java.
结束
Scala不需要复杂高深的理论或学术知识.一个普通的开发者就可以在下一个企业WEB应用里面使用它.它具有JAVA所缺乏的简洁明了的语法,并且保留了JAVA的高效与可靠性.
译者注:本文中的HelloWorld2还有一下一些版本
Java代码
| object HelloWorld{ def main(args: Array[String]) { println(args.mkString(" ")) } } object HelloWorld{ def main(args: Array[String]) { println(args.mkString(" ")) } } |
Java代码
| object HelloWorld{ def main(args: Array[String]) { println(args.reduceLeft((a:String,b:String)=>a+" "+b)) } } object HelloWorld{ def main(args: Array[String]) { println(args.reduceLeft((a:String,b:String)=>a+" "+b)) } } Java代码 object HelloWorld{ def main(args: Array[String]) { println(args.reduceLeft[String](_+" "+_)) } } |
考虑到这篇文章中使用的例子不是那么有吸引力,我摘一个引自 Scala官方网的例子:快速排序(这个例子其实也不怎么好,主要体现的是Scala中函数式编程的能力).
首先是用Scala语言写的Java风格的快速排序的代码
Java代码
| def sort(xs: Array[Int]) { def swap(i: Int, j: Int) { val t = xs(i); xs(i) = xs(j); xs(j) = t } def sort1(l: Int, r: Int) { val pivot = xs((l + r) / 2) var i = l; var j = r while (i <= j) { while (xs(i) < pivot) i += 1 while (xs(j) > pivot) j -=1 if (i <= j) { swap(i, j) i += 1 j -= 1 } } if (l < j) sort1(l, j) if (j < r) sort1(i, r) } sort1(0, xs.length 1) } def sort(xs: Array[Int]) { def swap(i: Int, j: Int) { val t = xs(i); xs(i) = xs(j); xs(j) = t } def sort1(l: Int, r: Int) { val pivot = xs((l + r) / 2) var i = l; var j = r while (i <= j) { while (xs(i) < pivot) i += 1 while (xs(j) > pivot) j -=1 if (i <= j) { swap(i, j) i += 1 j -= 1 } } if (l < j) sort1(l, j) if (j < r) sort1(i, r) } sort1(0, xs.length 1) } |
然后是函数式风格的代码:
Java代码
| def sort(xs: Array[Int]): Array[Int] = if (xs.length <= 1) xs else { val pivot = xs(xs.length / 2) Array.concat( sort(xs filter (pivot >)), xs filter (pivot ==), sort(xs filter (pivot <))) } |
相关专题
- Java环境安装配置 (5845篇文章)
- Java编程开发手册 (8526篇文章)
- Java基础知识:初学者必须理解的六大问题 (76次浏览)
- 九个非常值得一看的MySpace程序 (72次浏览)
- Java基础知识:谈谈简单Hibernate入门 (58次浏览)
- struts验证框架开发详解 (48次浏览)
- 关于提高自己水平的十大技术 (47次浏览)
- Flex测试工具RIATest Beta版发布 (45次浏览)
- 100行Java代码构建一个线程池。 (40次浏览)
- JAVA基础知识:JAVA变量类型之间的相互转换 (33次浏览)
- 什么是面向接口编程 (29次浏览)
- 动态java代码的使用方法 (28次浏览)



