论坛首页 Java企业应用论坛

Inside Scala - 01 - Hello Scala

浏览 7187 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (11) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-10-15   最后修改:2009-10-15

Andrew Hunt和David Thomas在他们的书The Pragmatic Programmer中建议说:每个程序员每年至少应该学习一门新的语言.

我正在学Scala. 我发现对我来说学习一门新语言最大的障碍就是我不一定马上就能够在工作中用到它. 所以在我看完一本介绍新语言的书(而没有写过一行代码)之后,我以为自己了解了这门语言,但是过不了多久就忘光了. 为了能够强迫自己写点东西,也为了把自己学到的一些东西分享出来(我看帖从来不回),我打算发几篇帖子. 如果我能坚持下来的话,我想写成一个系列,其中的每一篇帖子的内容可能都很简单. 系列的名字我打算叫Inside Scala. 当然了,离真正Inside还远着呢,不过实在想不出更合适的名字了. 好了,废话不多说,马上开始这个系列的第一篇文章吧!

 

 

参考资料:

Programming Scala - Tackle Multi-Core Complexity on the Java Virtual Machine

Programming Scala(Animal Guide)

Programming in Scala: A Comprehensive Step-by-step Guide

 

 

 

先来看看最简单的Hello World程序在Scala中长什么样:

object Hello {
  def main(args: Array[String]) = println("Hello Scala!")
}

 

把上面这几行scala代码保存到一个文件中,随便起个名字(比如hello.scala),然后打开一个命令行窗口,将当前路径切换到hello.scala文件所在的目录,
然后执行如下命令编译这个文件:

scalac Hello.scala

 

要执行这个程序的话,只要运行:

scala Hello
 

 

 

首先来学习一些最简单的Scala语言知识以便理解这个简单Scala程序

 

  1. object是Scala的一个关键字(keyword),用来定义一个单例对象(Singleton).可以简单的认为Scala在背后为我们定义了一个匿名类,并顺手实例化出该类的一个实例.如果把object关键字换成class则定义了一个普通的Scala类.
  2. Scala是面向对象的,而且比Java更纯粹,在Scala的关键字列表里更本就找不到static. 为了在Scala中模拟属于类(而不是某个对象)的字段和方法,需要用到companion对象. 简单的说,如果某个单例对象和某个类同名且定义在同一个包和源文件中的话,那么这个类和相应的单例对象互为companion
  3. Scala规定了应用程序入口,如果一个单例类中定义了一个方法,且该方法签名满足以下几点,那么这个单例类就可以作为Scala应用程序的入口

                a)方法名是main
                b)方法的返回值类型是Unit(相当于Java中的void

                c)方法有唯一参数,且其类型为Array[String](相当于Java中的String[])
            换句话说,这个方法会被翻译为Java中的main方法。

  4. Scala方法定义以关键字def开头.
  5. 在Scala方法定义中,方法的返回类型可以省略,大部分时候Scalac可以为我们推断出这个方法的返回类型. 所以上面main方法定义等价于:
    def main(args: Array[String]): Unit = println("Hello, Scala!")
    
  6. 可以暂时认为等号(=)开始了Scala的方法体定义,方法体定义通常包含在一对儿大括号中,但是如果一个方法体简单到只有一个表达式的话,那么大括号可以省略.
  7. 和JavaScript类似,在Scala中用分号结束语句是可选的. 所以上面的main方法实际上可以写成这样:
    def main(args: Array[String]): Unit = { println("Hello, Scala!"); }
    
  8. 和Ruby类似,在Scala方法定义中,return是可选的,通常方法体的最后一个表达式的求值结果就是这个方法的返回值.
  9. 可以在Scala程序中直接掉用println()函数.
  10. Array[String]表示一个元素类型为StringArray. Scala有像Java一样的范型机制,只不过用方括号取代尖括号.

 

那么这段代码到底是如何工作的呢?让我们先看看scalac为我们生成的class文件,在hello.scala文件所在的目录可以看到,scalac编译出了两个class文件:Hello.class和Hello$.class,用JAD反编译一下看看.

 

Hello.class :

import java.rmi.RemoteException;

public final class Hello {
    public static final void main(String args[]) {
        Hello$.MODULE$.main(args);
    }

    public static final int $tag() throws RemoteException {
        return Hello$.MODULE$.$tag();
    }
}

 

 Hello$.class :

import java.rmi.RemoteException;
import scala.Predef$;
import scala.ScalaObject;

public final class Hello$ implements ScalaObject {

    public Hello$() {
    }

    public void main(String args[]) {
        Predef$.MODULE$.println("Hello Scala!");
    }

    public int $tag() throws RemoteException {
        return scala.ScalaObject.class.$tag(this);
    }

    public static final Hello$ MODULE$ = this;

    static 
    {
        new Hello$();
    }
}

 

 

其中好像有些RMI相关的代码,暂时与我们的讨论无关,我把它们先去掉. 另外,Hello$.class的MODULE$ 初始化好像有点问题,我猜是JAD的问题,我把它们修正如下:

 

Hello.class :

public final class Hello {
    public static final void main(String args[]) {
        Hello$.MODULE$.main(args);
    }
}

 

 

Hello$.class :

import scala.Predef$;
import scala.ScalaObject;

public final class Hello$ implements ScalaObject {
    public Hello$() {}

    public void main(String args[]) {
        Predef$.MODULE$.println("Hello Scala!");
    }

    public static final Hello$ MODULE$ = new Hello$();
}

 

 

结论已经很明显了:

  1. Scalac为我们编写了一个Hello$类,这个类就是藏在单例对象Hello背后的那个类
  2. Scalac为Hello$类定义了一个staticMODULE$来支持Scala语言层面的单例概念
  3. println()方法实际上是Predef$.MODULE$.println()方法的缩写. 我们可以猜到Predef其实是Scala语言预定义的一个单例类,其中定义了一个println()方法,并且Scala自动import了这个单例对象
  4. Scalac还编写了一个Hello类,并且在这个类中定义了一个Java的main()方法,这个方法只是简单的调用Hello单例类的main()方法而已

 

   发表时间:2009-10-15  
lz 请到 helloworld 达人圈子报到
0 请登录后投票
   发表时间:2009-10-16  
顶NS,helloworld 达人圈子欢迎楼主~
话说Scala的return跟Ruby的确实像,都能做non-local return:
先定义foo函数:
def foo(i: Int): Int = {
  val bar = (j: Int) => { return j + 1 }
  bar(i)
  println("foo(" + i + ") end")
  i
}

然后调用它:
foo(3)

发现就得到了返回值是4而已,那句println()显然也没执行。这就是non-local return...
0 请登录后投票
   发表时间:2009-10-16  
哈哈,被你们两个抢先啦,我也来凑个热闹。支持一下楼主,欢迎到Scala圈子来看看。

@RednaxelaFx:你的代码很有趣,的确像你说的一样。为什么返回不是i的3呢,可否简单说明一下何为non-local return。
0 请登录后投票
   发表时间:2009-10-16   最后修改:2009-10-16

def foo(i: Int): Int = {
  val bar = (j: Int) => { return j + 1 }
  bar(i)
  println("foo(" + i + ") end")
  i
}




好像在一本Ruby的书中看到过,把第二行那个return关键字去掉就会打印并返回3了。
0 请登录后投票
   发表时间:2009-10-16   最后修改:2009-10-16
spider-dog 写道
def foo(i: Int): Int = {
  val bar = (j: Int) => { return j + 1 }
  bar(i)
  println("foo(" + i + ") end")
  i
}

好像在一本Ruby的书中看到过,把第二行那个return关键字去掉就会打印并返回3了。

没错

fineqtbull 写道
@RednaxelaFx:你的代码很有趣,的确像你说的一样。为什么返回不是i的3呢,可否简单说明一下何为non-local return。

一般的函数调用-返回关系,应该在最后从被调用方返回到直接调用方,这就是一般的local return。
Non-local return则是跳过一层或多层的直接调用方,返回到更外层去。
上面的例子里,bar与一个匿名函数绑定。如果return实现的是local return语义,那么foo()中调用bar()后控制流仍然应该返回foo();但实际上Scala里的return有non-local return语义,无论嵌套在多深的匿名函数里,return都会把控制流返回到最靠近的具名函数的调用方。
def foo(i: Int): Int = {
  val bar = (j: Int) => {
    ((k: Int) => { return k + 1 })(j + 1)
    println("bar(" + j + ") end")
    j + 1
  }
  bar(i)
  println("foo(" + i + ") end")
  i
}

改成这样,return仍然是跳到foo的调用方去,而不是返回到bar()里。

所以在Scala(和Ruby)里,没事就别写return,特别是要小心匿名函数里写return。与其把return看成可选的,还不如抛弃命令式语言的“逐个命令执行”的思路,改用“面向表达式”的思路来看:一个表达式有值;一组表达式串在一起时取最后一个表达式的值作为整组的值。
2 请登录后投票
   发表时间:2009-10-16  
这个算脚本语言吗?
0 请登录后投票
   发表时间:2009-10-16  
@RednaxelaFX 谢谢说明,原来匿名和具名函数还有这样的差别呀,return也很强大,可以跨越多个方法调用。另外我知道有一个相似的return问题,用于try语句中,是不是和non-local return也有关系呀。
scala> def f(): Int = try { return 1 } finally { return 2 }
f: ()Int

scala> f()
res18: Int = 2

上述代码较符合命令式编程的思路,最后的返回值是finally语句中的结果。
scala> def g(): Int = try { 1 } finally { 2 }
g: ()Int

scala> g()
res19: Int = 1

而上述代码就比较“独特”了,与return语句不同,返回的是try中的1。
0 请登录后投票
   发表时间:2009-10-16  
return的作用是强制改变控制流,但是finally块比它更霸道。try块后如果跟有finally块,则即便try块里有return,也必须在正常或非正常离开try后先进入finally块执行。此时如果finally块里有return,控制流就会被它跳回到caller。
用表达式观点看try...finally,整个表达式的值取决于try块里的最后一个表达式的值,除非有return。
0 请登录后投票
   发表时间:2009-10-16  
fineqtbull 写道

scala> def g(): Int = try { 1 } finally { 2 }
g: ()Int

scala> g()
res19: Int = 1

而上述代码就比较“独特”了,与return语句不同,返回的是try中的1。


这个谁能解释一下?
编译成Java代码会是什么样子?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics