目录 [−]
Scala是一门多范式(multi-paradigm)的编程语言。 Scala源代码被编译成Java字节码,所以它可以运行于JVM之上,并可以调用现有的Java类库。 洛桑联邦理工学院的Martin Odersky于2001年基于Funnel的工作开始设计Scala。Funnel是把函数式编程思想和佩特里网相结合的一种编程语言。Odersky先前的工作是Generic Java和javac。Java平台的Scala于2003年底/2004年初发布。该语言第二个版本,v2.0,发布于2006年3月。
Scala是面向对象的,比Java更彻底 一切皆为对象, 数值,函数都是对象
全部支持函数式编程 包括函数即是对象,lambda,currying, type inference, immutability, lazy evaluation, and pattern matching
强大的静态类型系统 algebraic data types, covariance and contravariance, higher-order types, anonymous types, generic classes, upper and lower type bounds, inner classes and abstract types as object members, compound types, explicitly typed self references , views and polymorphic methods
其它Java不支持的功能: operator overloading, optional parameters, named parameters, raw strings, and no checked exceptions
2009年4月,Twitter宣布他们已经把大部分后端程序从Ruby迁移到Scala,其余部分也打算要迁移。这里有一篇文章解释Twitter为什么使用Scala编程语言。Engineer-to-Engineer Series Programming Languages Programming Languages Scala Talks Coursera把Scala作为服务器语言使用。Why we love Scala at Coursera 一些Scala学习资料:
大数据生态圈中的Kafka和Spark都是由Scala开发的,这也是我为什么学习Scala的原因之一。 作为一个十多年Java程序员,同时在学习Scala和go这两种语言。 学习的过程中感觉go语言太好学了, 入手很快, 而Scala的语法和类C语言如Java,C#等很不一样, 很多语法的技巧在里面。 基于此,我特地整理了这篇文章。 简单扼要的介绍Scala语言的知识点,尤其是和Java不太一样的地方。
变量声明 1 2 3 var x = 5 var x:Double = 5 val y = 7
var 声明变量, val 声明常量, 类型可省略, Scala可以推断出数据类型
函数 1 2 def f (x: Int ) = { x*x }def f (x: Any ): Unit = println(x)
定义函数, 返回值类型可省略, =等号后面可以是块定义或者表达式。
1 2 3 4 names foreach (n => println(n)) names mkString "," optStr getOrElse "<empty>"
一个参数时可以使用infix 写法infix 操作符可以定义如下:
1 2 3 4 5 6 7 8 9 10 11 class MyBool (x: Boolean ) { def and (that: MyBool ): MyBool = if (x) that else this def or (that: MyBool ): MyBool = if (x) this else that def negate : MyBool = new MyBool (!x) def not (x: MyBool ) = x negate; def xor (x: MyBool , y: MyBool ) = (x or y) and not(x and y) } 5. +(3 ); 5 + 3 (1 to 5 ) map (_*2 )
1 2 def f (x: R )def f (x: => R )
第一个call-by-value ,第二个call-by-name(lazy parameters)
1 2 (1 to 5 ).map(_*2 ) (1 to 5 ).reduceLeft( _+_ )
1 2 3 (1 to 5 ).map(2 *) 等价 (1 to 5 ).map(2 * _)
1 2 (1 to 5 ).map { val x=_*2 ; println(x); x } (1 to 5 ) filter {_%2 == 0 } map {_*2 }
1 2 def compose (g:R =>R , h:R =>R ) = (x:R ) => g(h(x))val f = compose({_*2 }, {_-1 })
1 2 3 4 val zscore = (mean:R , sd:R ) => (x:R ) => (x-mean)/sd def zscore (mean:R , sd:R ) = (x:R ) => (x-mean)/sd def zscore (mean:R , sd:R )(x:R ) = (x-mean)/sd val normer = zscore(7 , 0.4 )_
1 def sum (args: Int *) = args.reduceLeft(_+_)
包package 1 2 3 4 5 6 7 8 import scala.collection._ import scala.collection.Vector import scala.collection.{Vector , Sequence } import scala.collection.{Vector => Vec28 } import java.util.{Date => _, _} package pkg at start of filepackage pkg { ... }
一般语句后面可以省略 ;
数据结构 1 2 (1 ,2 ,3 ) var (x,y,z) = (1 ,2 ,3 )
1 2 var xs = List (1 ,2 ,3 )xs(2 )
1 2 3 1 :: List (2 ,3 )List (1 , 2 ) ::: List (2 , 3 )List (1 , 2 ) ++ Set (3 , 4 , 3 )
一些特殊的操作符
1 2 1 to 5 same as 1 until 6 1 to 10 by 2
字符串 1 2 3 val name = "James" println(s"Hello, $name " ) println(s"1 + 1 = ${1 + 1} " )
s前缀, 替换字符串中的变量或表达式
1 2 3 val height = 1.9 dval name = "James" println(f"$name %s is $height %2.2f meters tall" )
f前缀, printf风格的格式化
1 2 scala> raw"a\nb" res1: String = a\nb
raw前缀, 原始字符,不转义
1 2 scala> "a" .r res1: scala.util.matching.Regex = a
r后缀, 正则表达式
1 2 3 4 implicit class JsonHelper (private val sc: StringContext ) extends AnyVal { def json (args: Any *): JSONObject = ... } val x: JSONObject = json"{ a: $a }"
自定义的字符串拦截器
控制流程 1 2 3 if (check) happy else sadif (check) happy if (check) happy else ()
1 2 while (x < 5 ) { println(x); x += 1 } do { println(x); x += 1 } while (x < 5 )
1 2 3 4 5 6 import scala.util.control.Breaks ._breakable { for (x <- xs) { if (Math .random < 0.1 ) break } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 for (x <- xs if x%2 == 0 ) yield x*10 xs.filter(_%2 == 0 ).map(_*10 ) for ((x,y) <- xs zip ys) yield x*y (xs zip ys) map { case (x,y) => x*y } for (x <- xs; y <- ys) yield x*y xs flatMap {x => ys map {y => x*y}} for (x <- xs; y <- ys) { println("%d/%d = %.1f" .format(x,y, x*y)) } for (i <- 1 to 5 ) { println(i) } for (i <- 1 until 5 ) { println(i) }
模式匹配 1 (xs zip ys) map { case (x,y) => x*y }
1 2 3 4 5 val v42 = 42 Some (3 ) match {case Some (`v42`) => println("42" )case _ => println("Not 42" )}
1 2 3 4 5 val UppercaseVal = 42 Some (3 ) match {case Some (UppercaseVal ) => println("42" )case _ => println("Not 42" )}
case class自动生成equals和toString,参数相同则==返回true
面向对象 没有访问修饰符的class或者类成员默认都是public类型的。
1 2 3 class C (x: R ) class C (private val x: R )var c = new C (4 )
1 2 3 class C (val x: R )var c = new C (4 )c.x
1 2 3 4 5 6 7 class C (var x: R ) {assert(x > 0 , "positive please" ) var y = x val readonly = 5 private var secret = 1 def this = this (42 ) }
1 2 3 4 5 6 new { ... }abstract class D { ... }class C extends D { ... } class D (var x: R )class C (x: R ) extends D (x ) object O extends D { ... }
1 2 3 4 5 6 7 trait T { ... } class C extends T { ... } class C extends D with T { ... }trait T1 ; trait T2 class C extends T1 with T2 class C extends D with T1 with T2 class C extends D { override def f = ...}
1 2 3 4 5 6 new java.io.File ("f" ) List (1 ,2 ,3 ) classOf[String ] x.isInstanceOf[String ] x.asInstanceOf[String ] x: String
1 2 3 4 5 6 final class A { final val x = 1 var y = 2 } sealed class B
final和sealed
对于内部类, inst1.InnerClass1 和inst2.InnerClass1是不同的类型,这和Java不一样。 如果想使用相同的类型,使用Class#InnerClass1
泛型 1 def mapmake [T ](g:T =>T )(seq: List [T ]) = seq.map(g)
1 2 3 4 5 6 class Stack [T ] {var elems: List [T ] = Nil def push (x: T ) { elems = x :: elems }def top : T = elems.headdef pop () { elems = elems.tail }}
1 2 3 4 5 6 7 class A class B extends A def test [T <: A ](t: T ) = {}test(new A ) test(new B )
1 2 3 4 5 6 7 class A class B extends A class C extends B def test [T <: A ](t: T ) = {}test[A ](new A ) test[C ](new C )
1 2 3 4 5 6 7 8 9 10 class A class B extends A class C extends B class Test [T ](x: T )val c = new Test (new C )val t:Test [B ] = c val a = new Test (new A )val t:Test [B ] = a
总结: 1) 协变 [+T], covariant (or “flexible”) in its type parameter T,类似Java中的(? extends T), 即可以用T和T的子类来替换T,里氏替换原则。 2) 不变 不支持T的子类或者父类,只知支持T本身。 3) 逆变 [-T], contravariant, 类似(? supers T) 只能用T的父类来替换T。是逆里氏替换原则。 4) 上界 只允许T的超类U来替换T。 [U >: T] 5) 下界 只允许T的子类U来替代T。 [U <: T]
注解 1 2 3 4 @interface SourceURL {public String value(); public String mail() default "" ; }
使用
1 2 3 4 5 6 7 8 @interface Source {public String URL (); public String mail(); } @Source (URL = "http://coders.com/" ,mail = "support@coders.com" ) class MyScalaClass ...
简写(对于属性名为value的特殊属性)
1 2 3 4 5 6 7 8 9 10 11 12 @interface SourceURL { public String value(); public String mail() default "" ; } @SourceURL ("http://coders.com/" ) class MyScalaClass ..@SourceURL ("http://coders.com/" ,mail = "support@coders.com" ) class MyScalaClass .
Implicit
implicit parameters 隐式参数 如果参数定义为implicit,那么在调用的如果没设置, 那么参数会自动提供。 隐式参数与缺省参数是完全不一样的。缺省参数是函数定义方设定了一个缺省值,在调用者没有指明时将使用该缺省值。 隐式参数则不同,最终是会由调用方指定参数值,只是不一定在调用的语句里指定而已。编译器在发现缺少隐式参数时,会在程序范围内寻找符合类型的隐式值,如果找不到则编译会失败。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 abstract class Logger {def log (s: String )}class FileLogger extends Logger { def log (s: String ) {println("Log in file: " + s)} } class StdoutLogger extends Logger { def log (s: String ) {println("Stdout: " + s)} } def Add (a: Int , b: Int )(implicit logger: Logger ) { val sum = a + b logger.log("%d + %d = %d" .format(a, b, sum )) } implicit val log = new FileLogger Add (1 ,2 )Add (2 ,3 )(new StdoutLogger )
如果上述代码没有implicit val log = new FileLogger这一句,在代码范围内也没有其他的Logger类型的implicit值,编译器会报错. 反之,如果能找到Logger类型的隐式值,编译器会将该隐式值作为参数传递过去。
implicit class 隐式类 A new language construct is proposed to simplify the creation of classes which provide extension methods to another type.
1 2 3 4 implicit class RichInt (n: Int ) extends Ordered [Int ] {def min (m: Int ): Int = if (n <= m) n else m... }
被转换为
1 2 3 4 5 class RichInt (n: Int ) extends Ordered [Int ] {def min (m: Int ): Int = if (n <= m) n else m... } implicit final def RichInt (n: Int ): RichInt = new RichInt (n)
implicit method 隐式转换 有时候,你并不需要指定一个类型是等/子/超于另一个类,你可以通过转换这个类来伪装这种关联关系。一个视界指定一个类型可以被“看作是”另一个类型。这对对象的只读操作是很有用的。 隐函数允许类型自动转换。更确切地说,在隐式函数可以帮助满足类型推断时,它们允许按需的函数应用。例如:
1 2 implicit def strToInt (x: String ) = x.toIntval y: Int = "123"
view view,就像类型边界,要求对给定的类型存在这样一个函数。您可以使用<%指定类型限制,例如:
1 class Container [A <% Int ] { def addIt (x: A ) = 123 + x }
这是说 A 必须“可被视”为 Int 。
方法可以通过隐含参数执行更复杂的类型限制。例如,List支持对数字内容执行sum,但对其他内容却不行。可是Scala的数字类型并不都共享一个超类,所以我们不能使用T <: Number。相反,要使之能工作,Scala的math库对适当的类型T 定义了一个隐含的Numeric[T]。 然后在List定义中使用它:
1 sum[B >: A ](implicit num: Numeric [B ]): B
如果你调用List(1,2).sum(),你并不需要传入一个 num 参数;它是隐式设置的。但如果你调用List("whoop").sum(),它会抱怨无法设置num。 在没有设定陌生的对象为Numeric的时候,方法可能会要求某种特定类型的“证据”。这时可以使用以下类型-关系运算符:
A =:= B
A 必须和 B相等
A <:< B
A 必须是 B的子类
A <%< B
A 必须可以被看做是 B
1 class Container [A ](value: A ) { def addIt (implicit evidence: A =:= Int ) = 123 + value }
空对象Nil,Null,null,Unit,Nothing,None
Nothing 是trait,定义为:final trait Nothing extends Any。Nothing处于Scala类型体系的最底层,是所有类型的子类型,Nothing没有实例。
Null 是trait,定义为:final trait Null extends AnyRef 。Null是所有引用类型的子类型,唯一的一个实例是null。
null是Null的实例,类似Java中的null
Nil 是case object,定义为case object Nil extends List[Nothing], 代表一个空list,长度为0。由于Scala中的List是协变的,因此无论T是何种类型,Nil都是List[T]的实例。
None 是case object,定义为:case object None extends Option[Nothing],代表不存在的值。Option有两个实例。None和Some
Unit 是class,定义为:abstract final class Unit extends AnyVal。Unit跟Java中的void相当,当一个方法不返回任何值的时候,那么方法的类型是Unit。Unit唯一的一个实例是().