變數 String yourPast = "Good Java Programmer"; val yourPast : String = "Good Java Programmer" val yourPast = "Good Java Programmer" var yourFuture = "Go ...
變數 |
String yourPast = "Good Java Programmer"; |
val yourPast : String = "Good Java Programmer" val yourPast = "Good Java Programmer" var yourFuture = "Good Java Programmer" 自動類型推斷;可變、不可變變數 class Money(amount:Int) amount不是成員變數 class Money(val amount:Int) val notMuch = new Money(2) notMuch.amount class Money(var amount:Int) val notMuch = new Money(2) notMuch.amount=3 |
case classes |
public class Money { private Integer amount; private String currency; public Money(Integer amount, String currency) { this.amount = amount; this.currency = currency; } public Integer getAmount() { return amount; } public void setAmount(Integer amount) { this.amount = amount; } public String getCurrency() { return currency; } public void setCurrency(String currency) { this.currency = currency; } @Override public int hashCode() { int hash = 5; hash = 29 * hash + (this.amount != null ? this.amount. hashCode() : 0); hash = 29 * hash + (this.currency != null ? this.currency. hashCode() : 0); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Money other = (Money) obj; return true; } @Override public String toString() { return "Money{" + "amount=" + amount + ", currency=" + currency + '}'; } public Money add(Money other) { return new Money(this.amount + other.amount, this.currency); } } |
case class Money(amount:Int=1, currency:String="USD") two immutable fields the fields declared in Scala classes are public case class Money(private val amount: Int, private val currency: String) to make them private instead, or used var instead of val to make the fields mutable. val defaultAmount = Money() val fifteenDollars = Money(15,"USD") val fifteenDollars = Money(15) val someEuros = Money(currency="EUR") case class Money(val amount:Int=1, val currency:String="USD"){ def +(other: Money) : Money = Money(amount + other.amount) } Money(12) + Money(34) |
collections |
Scala collections are, by default, immutable val numbers = List(1,2,3,4,5,6) val reversedList = numbers.reverse val onlyAFew = numbers drop 2 take 3 cons operator val numbers = 1 :: 2 :: 3 :: 4 :: 5 :: 6 :: Nil val simpleList = Nil.::(6) val twoElementsList = List(6).::(5) 列表串接 val concatenatedList = simpleList ::: twoElementsList val things = List(0,1,true) AnyVal val things = List(0,1,true,"false") Any 複雜對象列表 val amounts = List(Money(10,"USD"),Money(2,"EUR"),Money(20,"GBP"), Money(75,"EUR"),Money(100,"USD"),Money(50,"USD")) Filter val euros = amounts.filter(money => money.currency=="EUR") val euros = amounts.filter(x => x.currency=="EUR") val euros = amounts.filter(_.currency=="EUR") partition val allAmounts = amounts.partition(amt => amt.currency=="EUR") Tuples val euros = allAmounts._1 val everythingButEuros= allAmounts._2 val (euros,everythingButEuros) = amounts.partition(amt => amt.currency=="EUR") |
|
Map |
Map amounts = new HashMap<String,Integer>(); amounts.put("USD", 10); amounts.put("EUR", 2); |
val wallet = Map( "USD" -> 10, "EUR" -> 2 ) val someEuros = wallet("EUR") Option類型 val mayBeSomePounds = wallet.get("GBP") val updatedWallet = wallet + ("GBP" -> 20) 新的不可變 val tenDollars = "USD"-> 10 Tuple |
def increment = (x:Int) => x + 1 List(1,2,3,4).map(increment) List(1,2,3,4) map increment |
||
String Interpolation |
val many = 10000.2345 val amount = s"$many euros" 格式化寬度 val amount = f"$many%12.2f euros" val amount = s"${many*2} euros" val printedAmounts = amounts map(m=> s"${m.amount} ${m.currency}") |
|
groupBy |
groupBy method that transforms a collection into a Map collection: val sortedAmounts = amounts groupBy(_.currency) |
|
Scala UnitTest |
POM.xml maven依賴文件 • Dependency for the core scala-library: <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> <version>2.10.0</version> </dependency> • Dependency for scalatest (a framework for testing in Scala that supports JUnit and other styles; we will cover it in detail in Chapter 4, Testing Tools): <dependency> <groupId>org.scalatest</groupId> <artifactId>scalatest_2.10</artifactId> <version>2.0/version> <scope>test</scope> </dependency> • Dependency for JUnit to use Java Assert statements in our test case: <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> scala-maven-plugin <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <executions> <execution> <id>scala-compile-first</id> <phase>process-resources</phase> <goals> <goal>add-source</goal> <goal>compile</goal> </goals> </execution> <execution> <id>scala-test-compile</id> <phase>process-test-resources</phase> <goals> <goal>testCompile</goal> </goals> </execution> </executions> </plugin> 單元測試代碼 import org.junit._ import Assert._ class CustomerScalaTest { @Before def setUp: Unit = { } @After def tearDown: Unit = { } @Test def testGetCustomerId = { System.out.println("getCustomerId") val instance = new Customer() val expResult: Integer = null val result: Integer = instance.getCustomerId() assertEquals(expResult, result) } } |
|
Java/Scala Collection轉換 |
./activator console import java.util.Arrays val javaList = Arrays.asList(1,2,3,4) import scala.collection.JavaConverters._ val scalaList = javaList.asScala val javaListAgain = scalaList.asJava assert( javaList eq javaListAgain) |
|
JavaBean-style properties |
class Company(var name:String) val sun = new Company("Sun Microsystems") sun.name sun.name_=("Oracle") import scala.beans.BeanProperty class Company(@BeanProperty var name:String) val sun = new Company("Sun Microsystems") sun.getName() sun.setName("Oracle") |
|
OO |
class Customer ( var customerId: Int, var zip: String) { def this( zip: String) = this(0,zip) def getCustomerId() = customerId def setCustomerId(cust: Int): Unit = { customerId = cust } } val customer = new Customer("123 45") |
|
traits |
interface VIPCustomer { Integer discounts(); } class Customer(val name:String, val discountCode:String="N" ){ def discounts() : List[Int] = List(5) override def toString() = "Applied discounts: " + discounts.mkString(" ","%, ","% ") } trait VIPCustomer extends Customer { override def discounts = super.discounts ::: List(10) } trait GoldCustomer extends Customer { override def discounts = if (discountCode.equals("H")) super.discounts ::: List(20) else super.discounts ::: List(15) } object Main { def main(args: Array[String]) { val myDiscounts = new Customer("Thomas","H") with VIPCustomer with GoldCustomer println(myDiscounts) } } scala> Main.main(Array.empty) Applied discounts: 5%, 10%, 20% Note that the order in which traits are stacked is important. They are calling each other from right to left. GoldCustomer is, therefore, the first one to be called. |
|
Static companion objects |
object Main { def main(args: Array[String]) { println("Hello Scala World !") } } object Customer { def apply()= new Customer("default name") } class Customer(name:String) { … } |
|
exceptions |
public class ConversionSample { static Integer parse(String numberAsString) { Integer number = null; try { number = Integer.parseInt(numberAsString); } catch (NumberFormatExceptionnfe) { System.err.println("Wrong format for "+numberAsString); } catch (Exception ex) { System.err.println("An unknown Error has occurred"); } System.out.println("Parsed Number: "+number); return number; } |
def parse(numberAsString: String) = try { Integer.parseInt(numberAsString) } catch { case nfe: NumberFormatException => println("Wrong format for number "+numberAsString) case e: Exception => println("Error when parsing number"+ numberAsString) } 返回值 case nfe: NumberFormatException => println("Wrong format for number "+numberAsString); -1 case _: Throwable => println("Error when parsing number "+numberAsString) -1 scala.util.Left or scala.util.Right type case class Failure(val reason: String) def parse(numberAsString: String) : Either[Failure,Int] = try { val result = Integer.parseInt(numberAsString) Right(result) } catch { case _ : Throwable => Left(Failure("Error when parsing number")) } |
String customerLevel = null; if(amountBought > 3000) { customerLevel = "Gold"; } else { customerLevel = "Silver"; } |
val amountBought = 5000 val customerLevel = if (amountBought> 3000) "Gold" else "Silver" |
|
Scala的代碼風格 IDE |
http://docs.scala-lang.org/style/ Eclipse-based (including all the different versions of Eclipse, Typesafe's own bundled version known as Scala IDE as well as more commercial IDEs such as SpringSourceSTS), IntelliJ IDEA, and NetBeans. |
|
SBT |
Simple Build Tool (SBT) http://www.scala-sbt.org/ http://www.scala-sbt.org/0.13/tutorial/Hello.html 例子 http://www.scala-sbt.org/0.13/tutorial/Directories.html 目錄結構,Build.sbt是sbt的定義文件 |
|
SBT plugins |
https://github.com/typesafehub/sbteclipse 基於sbt生成eclipse使用的工程文件 https://github.com/mpeltonen/sbt-idea 基於sbt生成IntelliJ使用的工程文件 https://github.com/dcaoyuan/nbscala/wiki/SbtIntegrationInNetBeans 基於sbt生成Netbeans使用的工程文件 plugins.sbt中修改以生成 xsbt-web-plugin (available at https://github.com/JamesEarlDouglas/xsbt-web-plugin ), a useful plugin to create traditional web apps that runs on a servlet container (such as Jetty). v Plugins.sbt: addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "0.4.2") Build.sbt: name := "SampleProject" organization := "com.samples" version := "1.0" scalaVersion := "2.10.3" seq(webSettings :_*) libraryDependencies += "org.mortbay.jetty" % "jetty" % "6.1.22" % "container" libraryDependencies += "javax.servlet" % "servlet-api" % "2.5" % "provided" v sbt eclipse生成了eclipse工程 v 啟動web工程 > sbt > container:start v War包生成 > package |
|
sbt-assembly |
https://github.com/sbt/sbt-assembly v Plugins.sbt addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2") v assembly.sbt import AssemblyKeys._ // put this at the top of the file assemblySettings // your assembly settings here |
|
Scalariform |
plugins.sbt addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.0") SBT中運行compile or test:compile時,即按照scala風格格式化代碼 |
|
Scala Worksheets |
Eclipse中建立的菜單 每次保存時,可以看到執行的結果 |
|
Java基礎庫上scala的封裝例子 |
http://dispatch.databinder.net/Dispatch.html HttpClient上的封裝 build.sbt添加libraryDependencies += "net.databinder.dispatch" %% "dispatch-core" % "0.11.0" import dispatch._, Defaults._ val request = url("http://freegeoip.net/xml/www.google.com") val result = Http( request OK as.String) val resultAsString = result() def printer = new scala.xml.PrettyPrinter(90, 2) for (xml <- citiesAsXML) println(printer.format(xml)) |
|
for comprehension or for expression |
for (sequence) yield expression In the preceding code, sequence can contain the following components: • Generators: They drive the iteration and are written in the following form: element <- collection • Filters: They control the iteration and are written in the following form: if expression • Definitions: They are local variable definitions and are written in the following form: variable = expression for { elem <- List(1,2,3,4,5) } yield "T" + elem for { word <- List("Hello","Scala") char <- word } yield char.isLower for { word <- List("Hello","Scala") char <- word if char.isUpper } yield char or { word <- List("Hello","Scala") char <- word lowerChar = char.toLower } yield lowerChar |
|
測試 |
ScalaTest ( www.scalatest.org ) and Specs2 ( etorreborre.github.io/specs2/ ). ScalaCheck framework ( www.scalacheck.org ) Typesafe Activator包含必要的插件,項目根下執行> activator eclipse即生成eclipse工程 import org.scalatest.FunSuite class Test01 extends FunSuite { test("Very Basic") { assert(1 == 1) } test("Another Very Basic") { assert("Hello World" == "Hello World") } } > activator > test-only scalatest.Test01 (or scalatest.Test01.scala) >~test-only scalatest.Test02 文件修改後自動運行單元測試【continuous mode】 The continuous mode works for the other SBT commands as well, such as ~run or ~test 作為Junit的單元測試方式運行 import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner import org.scalatest.FunSuite @RunWith(classOf[JUnitRunner]) class MyTestSuite extends FunSuite ... www.seleniumhq.org 功能測試 package scalatest import org.scalatest._ import org.scalatest.selenium.WebBrowser import org.openqa.selenium.htmlunit.HtmlUnitDriver import org.openqa.selenium.firefox.FirefoxDriver import org.openqa.selenium.WebDriver class Test08 extends FlatSpec with Matchers with WebBrowser { implicit val webDriver: WebDriver = new HtmlUnitDriver go to "http://www.amazon.com" click on "twotabsearchtextbox" textField("twotabsearchtextbox").value = "Scala" submit() pageTitle should be ("Amazon.com: Scala") pageSource should include("Scala Cookbook: Recipes") } |
|
Web框架 |
alternatives to create web applications range from lightweight frameworks such as Unfiltered, Spray, or Scalatra to full-featured solutions such as the Lift or the Play Frameworks. |
|
Web Service |
Plugins.sbt addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" %"2.4.0") addSbtPlugin("org.scalaxb" % "sbt-scalaxb" % "1.1.2") resolvers += Resolver.sonatypeRepo("public") build.sbt import ScalaxbKeys._ name:="wssample" scalaVersion:="2.10.2" scalaxbSettings ibraryDependencies += "net.databinder.dispatch" %% "dispatch-core" %"0.11.0" libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M7" %"test" sourceGenerators in Compile <+= scalaxb in Compile packageName in scalaxb in Compile := "se.wssample" |
|
XML/JSON |
val books = <Library> <book title="Programming in Scala" quantity="15" price="30.00" /> <book title="Scala for Java Developers" quantity="10" price="25.50" /> </Library> import scala.xml._ val sameBooks = XML.loadString(""" <Library> <book title="Programming in Scala" quantity="15" price="30.00"/> <book title="Scala for Java Developers" quantity="10" price="25.50"/> </Library> """) val total = (for { book <- books \ "book" price = ( book \ "@price").text.toDouble quantity = ( book \ "@quantity").text.toInt } yield price * quantity).sum val books = <Library> { List("Programming in Scala,15,30.00","Scala for Java Developers,10,25.50") map { row => row split "," } map { b => <book title={b(0)} quantity={b(1)} price={b(2)} /> }} </Library> import scala.util.parsing.json._ val result = JSON.parseFull(""" { "Library": { "book": [ { "title": "Scala for Java Developers", "quantity": 10 }, { "title": "Programming Scala", "quantity": 20 } ]} } """) |
|
Futures and Promises |
Scala Improvement Process (SIP)SIP-14-Futures and Promises http://en.wikipedia.org/wiki/Futures_and_promises • async { <expression> } : In this construct, <expression> is the code to be executed asynchronously. • await { <expression returning a Future> } : This construct is included in an async block. It suspends the execution of the enclosing async block until the argument Future is completed. async future Actor |
|
Reactive Systems |
http://www.reactivemanifesto.org/ 反應式編程 |
|
Misc |
MongoDB database, we are going to discover how the Casbah Scala toolkit https://github.com/mongodb/casbah |
|
RPEL模式下拷貝部分代碼運行 scala> :paste // Entering paste mode (ctrl-D to finish) |
||