scala是基于java开发的,结合了面向对象编程技术及函数式编程技术。scala的静态类型避免了复杂程序的大部分BUG。它的JVM和Js运行环境让你能够构建高性能系统。html
scala -version
Scala有一个命令行叫作REPL,能够实现交互式编程。命令行键入scala
,进入交互式编程环境。java
$ scala scala> // 默认状况下每一个键入的表达式都将保存为一个新的类型。 // 注意在Scala中只有对象类型,Int也为对象。 scala> 2 + 2 res0: Int = 4 // 默认值能够被重用,注意结果显示变量的类型。 // res1值类型为Int,值为6 scala> res0 + 2 res1: Int = 6 // Scala是一个强类型语言。 // 可使用type指令在不执行表达式状况下检查表达式类型。 scala> :type (true, 2.0) (Boolean, Double) // REPL会话能够保存到本地目录 scala> :save /sites/repl-test.scala // 可使用load指令加载scala文件 scala> :load /sites/repl-test.scala Loading /sites/repl-test.scala... res2: Int = 4 res3: Int = 6 // 可使用h指令检查最近的输入历史 scala> :h? 1 2 + 2 2 res0 + 2 3 :save /sites/repl-test.scala 4 :load /sites/repl-test.scala 5 :h?
已经学会交互式编程了,如今开始学一点scala。正则表达式
// 这是单行注释 /* 这是多行注释 */ // 打印,并换行 println("Hello world!") println(10) // Hello world! // 10 // 打印,不换行 print("Hello world") print(10) // Hello world10 // 变量声明使用var或者val. // val声明变量不可变的,var声明变量可变。 val x = 10 // x是10 x = 20 // 报错 var y = 10 y = 20 // y如今是20
Scala是一个静态类型语言,注意到在上述声明中咱们并无指定类型。这是由于Scala具备类型推断的功能。大部分状况下Scala编译器能够推断变量的类型,因此能够不用每次都显示声明变量。如下是显示声明变量:编程
val z: Int = 10 val a: Double = 1.0 // 注意表达式自动从Int转换为Double, 结果是10.0,而不是10 val b: Double = 10 // Boolean值 true false // Boolean操做 !true // false !false // true true == false // false 10 > 5 // true // 常规操做 1 + 1 // 2 2 - 1 // 1 5 * 3 // 15 6 / 2 // 3 6 / 4 // 1 6.0 / 4 // 1.5 6 / 4.0 // 1.5 "Scala字符串经过双引号括起来"//String 'a' // a 是一个Scala 字符 // '单引号字符串并不存在 // Strings对象有经常使用的Java字符串对象的方法。 "hello world".length "hello world".substring(2, 6) "hello world".replace("C", "3") // Strings对象有额外的Scala方法. "hello world".take(5) "hello world".drop(5) // 支持字符串插值,注意前缀"s" val n = 45 s"We have $n apples" // => "We have 45 apples" // 插值字符串支持表达式 val a = Array(11, 9, 6) s"My second daughter is ${a(0) - a(2)} years old." // => "My second daughter is 5 years old." s"We have double the amount of ${n / 2.0} in apples." // => "We have double the amount of 22.5 in apples." s"Power of 2: ${math.pow(2, 2)}" // => "Power of 2: 4" // 使用f前缀实现格式化版本插值字符串 f"Power of 5: ${math.pow(5, 2)}%1.0f" // "Power of 5: 25" f"Square root of 122: ${math.sqrt(122)}%1.4f" // "Square root of 122: 11.0454" // 原生字符串,忽略特殊字符,使用raw前缀 raw"New line feed: \n. Carriage return: \r." // => "New line feed: \n. Carriage return: \r." // 有些字符如"须要使用\转义 "They stood outside the \"Rose and Crown\"" // => "They stood outside the "Rose and Crown"" // 三个双引号实现字符串多行编写 val html = """<form id="daform"> <p>Press belo', Joe</p> <input type="submit"> </form>"""
Functions定义方法:数组
def functionName(args...): ReturnType = { body... }
Scala中经常使用定义没有return关键字.函数体最后一个表达式就是返回值。
def sumOfSquares(x: Int, y: Int): Int = { val x2 = x * x val y2 = y * y x2 + y2 }
若是函数体只有一个表达式,{ }能够省略.数据结构
def sumOfSquaresShort(x: Int, y: Int): Int = x * x + y * y // 调用函数很简单 sumOfSquares(3, 4) // => 25
你可使用变量名来指定不一样传参顺序app
def subtract(x: Int, y: Int): Int = x - y subtract(10, 3) // => 7 subtract(y=10, x=3) // => -7
大部分状况下能够略写函数返回类型,编译器能够从最后一个表达式值推断出来less
def sq(x: Int) = x * x // 编译器推断返回值类型为Int
函数能够有默认值。dom
def addWithDefault(x: Int, y: Int = 5) = x + y addWithDefault(1, 2) // => 3 addWithDefault(1) // => 6
匿名函数ide
(x: Int) => x * x
不像def声明, 若是context很清晰,匿名函数的输入也能够省略。注意"Int => Int"表明一个函数输入类型为Int,返回类型也为Int。
//sq是一个函数类型变量 val sq: Int => Int = x => x * x // 调用匿名函数 sq(10) // => 100
若是匿名函数的变量仅使用一次,Scala提供一个极简写法,使用_表明入参。
val addOne: Int => Int = _ + 1 val weirdSum: (Int, Int) => Int = (_ * 2 + _ * 3) addOne(5) // => 6 weirdSum(2, 4) // => 16
Scala能够包含return关键字,仅在最里层使用,外层使用def关键字。
WARNING: 在Scala中使用return容易出错,尽力避免这种写法。 这种写法对匿名函数一点做用都没有,例如里指望foo(7)返回17,实际返回7
def foo(x: Int): Int = { val anonFunc: Int => Int = { z => if (z > 5) return z // 该行让foo返回z else z + 2 // 该行是anonFunc返回值 } anonFunc(x) + 10 // 该行是foo函数返回值 } foo(7) // => 7
1 to 5 val r = 1 to 5 //foreach循环 r.foreach(println) //合法表达式 r foreach println //合法表达式 (5 to 1 by -1) foreach (println)
一个while循环
var i = 0 while (i < 10) { println("i " + i); i += 1 }
一个do-while循环
i = 0 do { println("i is still less than 10") i += 1 } while (i < 10)
在Scala中定义回调函数是重复一个动做的惯用方法。回调函数须要显示声明返回类型,编译器没法推断。
//这里时Unit, 至关于Java中的void def showNumbersInRange(a: Int, b: Int): Unit = { print(a) if (a < b) showNumbersInRange(a + 1, b) } showNumbersInRange(1, 14)
val x = 10 if (x == 1) println("yeah") if (x == 10) println("yeah") if (x == 11) println("yeah") if (x == 11) println("yeah") else println("nay") println(if (x == 10) "yeah" else "nope") val text = if (x == 10) "yeah" else "nope"
支持的数据类型又数组,字典,集合,链表,元组
//数组 val a = Array(1, 2, 3, 5, 8, 13) a(0) // Int = 1 a(3) // Int = 5 a(21) // Throws an exception //字典 val m = Map("fork" -> "tenedor", "spoon" -> "cuchara", "knife" -> "cuchillo") m("fork") // java.lang.String = tenedor m("spoon") // java.lang.String = cuchara m("bottle") // Throws an exception //设定字典默认value值 val safeM = m.withDefaultValue("no lo se") // java.lang.String = no lo se safeM("bottle") //集合 val s = Set(1, 3, 7) // Boolean = false s(0) // Boolean = true s(1) //元组 (1, 2) (4, 3, 2) (1, 2, "three") (a, 2, "three") val divideInts = (x: Int, y: Int) => (x / y, x % y) // (Int, Int) = (3,1) val d = divideInts(10, 3) // 使用 _._n访问元组元素,n值是元素的索引值,基数为1。 d._1 // Int = 3 d._2 // Int = 1 // 一样的你可使用多元赋值,这样代码更可读。 val (div, mod) = divideInts(10, 3) div // Int = 3 mod // Int = 1
到目前为止本教程展现的都是简单表达式,这些表达式能够在REPL中快速测试,但这些表达式不能独立存在Scala文件中 ,Scala中容许存在的高级式为:
classe声明与其余语言相似,构造器参数在类名后面声明,初始化代码在类中。
class Dog(br: String) { // 构造器代码 //定义一个字段 var breed: String = br // 定义了一个方法,返回字符串 def bark = "Woof, woof!" // 字段和方法默认是public。"protected"、"private"关键字是容许的。 //定义私有方法 private def sleep(hours: Int) = println(s"I'm sleeping for $hours hours") // 抽象方法无需声明方法体. Dog类也须要声明为抽象类 // abstract class Dog(...) { ... } // def chaseAfter(what: String): String } val mydog = new Dog("greyhound") println(mydog.breed) // => "greyhound" println(mydog.bark) // => "Woof, woof!"
"object"关键字建立了一个类型和一个类型单例。能够直接使用Dog对象,无需手动实例化。这种区别相似于其余语言类方法和静态方法。注意object和class命名能够相同。
object Dog { def allKnownBreeds = List("pitbull", "shepherd", "retriever") def createDog(breed: String) = new Dog(breed) }
case classes是一些具备额外功能的类。初学者很容易混淆class和case class的使用。一般状况下classes用于实现封装,多态,及行为。类中字段是私有的,方法则是对外暴露。case classes主要目的是保存不可变数据,他们不多有方法。相似Java中的Enum。
case class Person(name: String, phoneNumber: String) // 建立一个新的实例. 注意cases classes不须要使用"new" val george = Person("George", "1234") val kate = Person("Kate", "4567") //直接访问 george.phoneNumber // => "1234" // 全部字段都相同便可为true,无需重写equals方法 Person("George", "1234") == Person("Kate", "1236") // => false // 易于拷贝 // otherGeorge == Person("George", "9876") val otherGeorge = george.copy(phoneNumber = "9876")
相似Java接口, traits定义对象类型和方法签名。scala容许实现部分方法。不容许有构造方法。Traits能继承其余traits或者无构造参数类。
trait Dog { def breed: String def color: String def bark: Boolean = true def bite: Boolean } class SaintBernard extends Dog { val breed = "Saint Bernard" val color = "brown" def bite = false } scala> b res0: SaintBernard = SaintBernard@3e57cd70 scala> b.breed res1: String = Saint Bernard scala> b.bark res2: Boolean = true scala> b.bite res3: Boolean = false
能够继承多个trait, class "extends" 第一个trait,关键字"with"能够添加额外的traits.
trait Bark { def bark: String = "Woof" } trait Dog { def breed: String def color: String } class SaintBernard extends Dog with Bark { val breed = "Saint Bernard" val color = "brown" } scala> val b = new SaintBernard b: SaintBernard = SaintBernard@7b69c6ba scala> b.bark res0: String = Woof
模式匹配是Scala中常用的功能。这里展现模式如何匹配case class。
//Scala中cases不须要break语句。 def matchPerson(person: Person): String = person match { // Then you specify the patterns: case Person("George", number) => "We found George! His number is " + number case Person("Kate", number) => "We found Kate! Her number is " + number case Person(name, number) => "We matched someone : " + name + ", phone : " + number }
在字符串后声明r方法来建立正则表达式。
val email = "(.*)@(.*)".r
模式匹配相似与C语言中的switch声明,但更加有用。在Scala中,你能够匹配更多。
def matchEverything(obj: Any): String = obj match { // 能够匹配值: case "Hello world" => "Got the string Hello world" // 能够按类型匹配: case x: Double => "Got a Double: " + x // 能够指定条件: case x: Int if x > 10000 => "Got a pretty big number!" // 匹配case classes case Person(name, number) => s"Got contact info for $name!" // 匹配正则表达式 case email(name, domain) => s"Got email address $name@$domain" // 匹配元组 case (a: Int, b: Double, c: String) => s"Got a tuple: $a, $b, $c" // 匹配数据结构 case List(1, b, c) => s"Got a list with three elements and starts with 1: 1, $b, $c" // 嵌套匹配 case List(List((1, 2, "YAY"))) => "Got a list of list of tuple" // 若是以前的都没匹配上,执行这个case case _ => "Got unknown object" }
事实上,你可使用"unapply"方法匹配任意的对象。这个功能容许你将整个函数定义为模式。
val patternFunc: Person => String = { case Person("George", number) => s"George's number: $number" case Person(name, number) => s"Random person's number: $number" }
Scala容许函数做为入参或者返回值类型。
//定义一个入参为Int,返回值为Int的函数 val add10: Int => Int = _ + 10 // List(11, 12, 13) add10函数做用在每一个元素上。 List(1, 2, 3) map add10 // 能够在列表上使用匿名函数 List(1, 2, 3) map (x => x + 10) // 若是仅有一个入参且入参仅使用一次,可使用_符号表明入参 List(1, 2, 3) map (_ + 10) // 若是匿名函数中仅使用传参,你甚至能够省略_ List("Dom", "Bob", "Natalia") foreach println
// 定义集合 // val s = Set(1, 3, 7) def sq: Int => Int = _*10 //执行map操做 s.map(sq) val sSquared = s.map(sq) //使用过滤函数 sSquared.filter(_ < 10) //执行reduce操做 sSquared.reduce (_+_) // 过滤函数传入函数类型入参 List(1, 2, 3) filter (_ > 2) // List(3),保留元素3 case class Person(name: String, age: Int) List( Person(name = "Dom", age = 23), Person(name = "Bob", age = 30) ).filter(_.age > 25) // List(Person("Bob", 30))
Scala中几乎全部容器都有 foreach
方法, 传入一个参数,返回值为Unit。
val aListOfNumbers = List(1, 2, 3, 4, 10, 20, 100) aListOfNumbers foreach (x => println(x)) aListOfNumbers foreach println // For comprehensions for { n <- s } yield sq(n) val nSquared2 = for { n <- s } yield sq(n) for { n <- nSquared2 if n < 10 } yield n for { n <- s; nSquared = n * n if nSquared < 10} yield nSquared
重大提醒: Implicits是Scala的一个强大功能,因此很容易被误用.
Scala初学者应当耐住诱惑不使用这些功能,直到不只可否理解如何工做,同时可以理解用法的最佳实践。在这个教程讲述这部分缘由是这个功能使用太广泛了,咱们没法避免使用一个没隐式功能的库,因此理解隐式功能对你颇有义。
任何值(vals, functions, objects等)均可以使用“implicit”关键字声明为隐式。
// 注意本节将使用第五节定义的Dog类。 implicit val myImplicitInt = 100 implicit def myImplicitFunction(breed: String) = new Dog("Golden " + breed)
implicit关键字不改变值自己行为。
// => 102 myImplicitInt + 2 // => "Golden Pitbull" myImplicitFunction("Pitbull").breed
区别在于当一段代码须要一个implicit值,全部定义为implicit的值均可行了。
// 函数入参定义一个隐式howMany字段 def sendGreetings(toWhom: String)(implicit howMany: Int) = s"Hello $toWhom, $howMany blessings to you and yours!" // 若是为howMany提供一个值,函数正常调用。 sendGreetings("John")(1000) // => "Hello John, 1000 blessings to you and yours!" // 但若是忽略这个值,须要使用另外一个隐式值, 这里是"myImplicitInt" sendGreetings("Jane") // => "Hello Jane, 100 blessings to you and yours!"
隐式函数参数让咱们能够模拟其余函数语言的模板类,而且有本身的缩写方法,如下两行表达含义相同。
// def foo[T](implicit c: C[T]) = ... // def foo[T : C] = ...
另外一种编译器寻找隐式表达式的另外一种状况是若是你调用obj.method(...),实际这个obj没有这个"method"方法,在这种状况下,若是有一个隐式转换表达式从类型A => B,A是obj的类型,B有一个方法叫"method",转换会自动进行。
// 因为有myImplicitFunction这个函数,下面语句是合法的。 "Retriever".breed // => "Golden Retriever" "Sheperd".bark // => "Woof, woof!"
以上两个字符串首先使用以前定义的隐式函数转换成Dog类实例,而后调用合适的方法。这个是极其有用的方法,可是也很差用,容易形成BUG。事实若是使用implicit函数,编译器会给你一个warning。因此除非你知道本身在干啥,不然永远不要使用隐式功能。
// 导入类 import scala.collection.immutable.List // 导入全部子包 import scala.collection.immutable._ // 导入多个类 import scala.collection.immutable.{List, Map} // 使用'=>'重命名导入 import scala.collection.immutable.{List => ImmutableList} // 导入全部类,除了Map和Set类 import scala.collection.immutable.{Map => _, Set => _, _} // 一样能够导入Java类。 import java.swing.{JFrame, JWindow} // 在一个scala程序中,程序入口定义在object中,该object仅包含一个方法。 // 单一方法, main: object Application { def main(args: Array[String]): Unit = { // stuff goes here. } }
一个文件可包含多多个class及object. 使用scalac编译
// 按行读取文件 import scala.io.Source for(line <- Source.fromFile("myfile.txt").getLines()) println(line) // 使用Java's PrintWriter写入文件 val writer = new PrintWriter("myfile.txt") writer.write("Writing line for line" + util.Properties.lineSeparator) writer.write("Another line here" + util.Properties.lineSeparator) writer.close()