| | |
变量
| 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 voidsetCurrency(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")
}
www.scalamock.org
|
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.
http://www.playframework.com/ http://www.playmodules.net
|
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)
|
| | |