抠腚爱揉曼 Coding Iron Man

11Aug/102

Scala笔记1

最近在看Scala,把笔记贴过来存档。

  • Scala基于JVM,通过scalac编译成JVM字节码。不同于Groovy(同样是跑在JVM上),Scala是静态类型的,因此可以支持泛型和多态等特性。Scala完全使用自己的规则创建,因此相对于Groovy来说学习成本更高(Groovy其实就是Java的强化?简化?版)。而且因为基于JVM,因此可以很好的和现有Java项目整合,在Scala中可以直接使用Java现成的无数类库。
  • Scala通过var和val定义变量。var定义可变量,val定义不可变量,类似Java中的final,一旦初始化赋值后就无法改变。Scala鼓励尽可能的使用val让类和方法获取原生的线程安全性,同时通过val和闭包(方法对象)的使用来支持函数式编程。当然Scala也可以使用Java抠腚们熟悉的命令式来书写。
  • 既然Scala支持FP,因此它确实支持尾递归优化。只不过受制于JVM,只能做到简单的尾递归优化,在比较复杂的情况下无法确实优化。下面的代码清单是一个尾递归的例子,递归了一亿次,一切正常。
    object Main {
      def main(args: Array[String]): Unit = {
        println(doSum(100000000L, 0))
      }
    
      def doSum(n: Long, sum: Long): Long = {
        if (n == 0L) {
          sum
        } else {
          doSum(n - 1, sum + n)
        }
      }
    }
    

  • Scala中万物皆对象,它不像Java那样割裂开原始类型和对象。同时Scala有自己的富包装类,比如RichInt,提供一些好用的方法如RichInt的to,RichString的reverse。但是由于富包装类的存在,会出现一个陷阱。见下面的代码清单。
    2.toString()			// 直接对一个整数调用toString方法
    2 to 3					// RichInt的to方法,返回一个Range对象用于迭代。这里语法有点奇怪,下面会提到。
    "ana".reverse == "ana"	// false 因为是RichString和String的比较。用==比较有违Java的直觉,下面会提到。
    
  • Scala中万物皆方法(好吧其实我指的是它没有操作符!而且方法其实也是对象),所有的+、-、*、/等操作符其实都是方法,Scala使用方法名第一个字符的优先级来取代操作符优先级。同时为了配合这个特性,Scala语法允许对无参数或者只有一个参数的方法调用省略点号和括号。下面的代码清单演示了这个特性。
    2.toString()	// ----
    2.toString		// 这都是同样的调用
    2 toString;		// --- 注意这里使用了分号,在必要的时候使用分号告诉Scala语句结束了
    				// --- 绝大部分时候Scala自己会判断到语句的结束,解放你的右手小拇指
    2 to 3			// ---
    2.to(3)			// 这两个也是一样的
    2 * 3			// ---
    (2).*(3)		// 这两个还是一样的,使用(2)是因为2.会被识别成Double:2.0
    

    基于这个特性,我们甚至可以在Scala之上构筑自己的语言。

  • Scala中的==操作和Java中的不同,==由Scala类结构最上层的Any实现为final的方法,其实现就是调用Java经典的equals方法。在Scala中==的表现永远和equals相同,!=则永远和equals相反。因此虽然==被声明为final而不可重载,但我们可以通过重载equals支持自定义类型的==操作。如果你需要Java中经典的==操作符的时候,你可以使用eq。下面的代码清单分别使用了这两个方法。
    object Main {
      def main(args: Array[String]): Unit = {
        val p1 = new Point(1, 1)
        val p2 = new Point(1, 1)
        println(p1 == p2)	// true ==会调用重载的equals方法
        println(p1.==(p2))	// true ==同样是个方法
        println(p1 eq p2)	// false Java中的引用比较
      }
    }
    
    private class Point(_x: Int, _y: Int) {
      def x = _x
      def y = _y
    
      override def toString: String = {
        x + ":" + y
      }
    
      override def equals(other: Any): Boolean = {
        other != null &&
          other.isInstanceOf[Point] &&
          {
            val o = other.asInstanceOf[Point]
            o.x == this.x && o.y == this.y
          }
      }
    }
    
  • 闭包、集合、元组、多行原始字符串等现代动态语言中的特性都存在与Scala中,而Java则都没有(集合较复杂而不好用)。同样的省略分号,自动返回最后一句语句的执行结果等简化语法的特性也存在与Scala中。同时Scala的类型推断机制使得静态类型的Scala也拥有动态类型语言那样的书写风格和表现力。而为了使类型推断更好的工作,应当尽量避免在代码中显示的return,如果使用了显示的return,则可能必须为方法声明返回类型。
  • Scala中的另一个陷阱是它的赋值操作返回值是Unit(你可以认为是Java中的void),因此像a=b=c=1这样的连续赋值会报错,应该使用下面代码清单的赋值方式。
    val a, b, c = 1				// a、b、c都会被初始化为Int:1
    val (d, e, f) = (1, 2, 3)	// d、e、f分别被初始化为Int:1、Int:2、Int:3
    

未完待续……

Posted by Anson

Tagged as: , Leave a comment
Comments (2) Trackbacks (0)
  1. CPP城续缘笑而不语…


Leave a comment

(required)

No trackbacks yet.