scala语法

xiaoxiao2021-02-28  84

根据documents记录一些零碎的知识点,学习一定程度后再总结一下。

1 basic

1.1 Variables and Values

Variables和Values分别用var和val表示,他们之间的区别是var可以重复赋值,而val赋值后不可改变,相当于const。

val x = 1+1 var y: Int = 1 + 1 y = 3

变量的type类似c++11中auto会自己推断,同时也可以显式地标明:

val x: Int = 1+1

用{}将表达式圈起来就是一个blocks,相当于一个作用域,最后的一行表达式的输出就是blocks的输出:

println({ val x = 1 + 1 x + 1 }) // 3

1.2 function

带参数的表达式就是函数。

匿名函数: ()括号内是参数:

(x: Int) => x + 1

named函数:

val getAns = () => 4

单个参数:

val add = (x: Int) => x + 1 println(add(1))

多个参数

val add = (x: Int, y: Int) => x + y

=>右边就是带有参数的表达式

高阶函数

高阶函数:其他函数做参数或者是其结果的函数。

c++只能调用函数指针,来实现上述形式。

def apply(f: Int => String, v: Int) = f(v)

1.3 method

method结构: def关键字,方法名,多个参数列表或者没有参数列表,返回类型,body

def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier

如果返回类型是Unit,那么就表示viod,只是scala每个表达式必须返回一个值,所以用Unit,表示没有什么信息。

1.4 Classes

类的构成:

类名,构造参数

class Greeter(prefix: String, suffix: String) { def greet(name: String): Unit = println(prefix + name + suffix) } val greeter = new Greeter("Hello, ", "!") greeter.greet("Scala developer") // Hello, Scala developer!

默认构造函数

class Point(var x: Int = 0, var y: Int = 0) val origin = new Point // x and y are both set to 0 val point1 = new Point(1) println(point1.x) // prints 1

默认构造下只想手动赋值非第一个的参数,需要指明参数名称

class Point(var x: Int = 0, var y: Int = 0) val point2 = new Point(y=2) println(point2.y) // prints 2

private,将变量与类外隔离;与c++类似可以用def方法读取私有变量

private[A]会增加可见范围到A。

val修饰的变量默认为公有的,没有val即为私有;

class Point { private var _x = 0 private var _y = 0 private val bound = 100 def x = _x def x_= (newValue: Int): Unit = { if (newValue < bound) _x = newValue else printWarning } def y = _y def y_= (newValue: Int): Unit = { if (newValue < bound) _y = newValue else printWarning } private def printWarning = println("WARNING: Out of bounds") }

抽象类:

abstract class Element { //abstract表示抽象类 def contents: Array[String] //没有函数体表示声明抽象方法 }

无参函数和属性, 统一访问模式

abstract class Element { def contents: Array[String] //组合Array[] def height: Int = contents.length //无参函数 def width: Int = if (height == 0) 0 else contents(0).length } //等同于 abstract class Element { def contents: Array[String] val height = contents.length //属性 val width = if (height == 0) 0 else contents(0).length }

1.5 case

该类在类前加case关键字,不可修改并且之间可以比较大小

case 类最基本形式: case calss + 名字 + 参数列表

实例化不需要new,因为该类有默认的apply方法构造。

case class Message(sender: String, recipient: String, body: String)//变量类型为val public val message1 = Message("guillaume@quebec.ca", "jorge@catalonia.es", "Ça va ?")//实例化该类不需要new关键字 message1.sender = "travis@washington.us" // val是immutable的

case实例的比较:比较基于结构而不是引用,虽然两者明显指向的对象不一样,但是仍然可以进行比较。

case class Message(sender: String, recipient: String, body: String) val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") val messagesAreTheSame = message2 == message3 // true

case的复制:

case class Message(sender: String, recipient: String, body: String) val message4 = Message("julien@bretagne.fr", "travis@washington.us", "Me zo o komz gant ma amezeg") val message5 = message4.copy(sender = message4.recipient, recipient = "claire@bourgogne.fr") message5.sender // travis@washington.us message5.recipient // claire@bourgogne.fr message5.body // "Me zo o komz gant ma amezeg"

因为可变和不可变的区别,所以使用val相对case来说更安全。

1.6 Traits

traits相当于Java接口,但是功能更加强大,因为他还可以定义属性和方法。

与generic class和abstract method结合:

trait Iterator[A] { def hasNext: Boolean def next(): A }

因为scala的类仅继承单一父类,但是特征却可以多重继承。

extend和with

extend, implicitly inherit the trait’s superclass

class Frog extends Philosophical { //mix in Philosophical trait override def toString = "green"//override子类覆写父类方法 }

with, 需要显式的指明超类

class Animal trait HasLegs class Frog extends Animal with Philosophical with HasLegs {//继承Animal,并mix in两个traits override def toString = "green" }

1.7 type

类型体系:

类型转换顺序:

编译时Scala自动对应到Java原始类型,提高运行效率。Unit对应java的void

隐式转换

implicit def foo(s:String):Int = Integer.parseInt(s) // 需要时把String->Int def add(a:Int, b:Int) = a+b add("100",8) // 108, 先把"100"隐式转换为100

1.8 各种关键字

1.8.1 require

def f(n:Int) = { require(n!=0, "n can't be zero"); 1.0/n }

1.8.2 application

不带命令行参数的简化main方法: object app1 extends Application { println("hello world") }

1.8.3 for循环

for (i <- 0 to n) foo(i) // 包含n,即Range(0,1,2,...,n,n+1) for (i <- 0 until n) foo(i) // 不包含n,即Range(0,1,2,3,...,n)

1.8.4 reduceLeft /foldLeft /scanLeft

def fac(n: Int) = 1 to n reduceLeft(_*_) fac(5) // 5*4*3*2 = 120 相当于: ((((1*2)*3)*4)*5) def sum(L: Seq[Int]) = L.foldLeft(0)(_ + _)//累加 def multiply(L: Seq[Int]) = L.foldLeft(1)(_ * _)//累积 List(1,2,3,4,5).scanLeft(0)(_+_) // (0,1,3,6,10,15) 相当于: (0,(0+1),(0+1+2),(0+1+2+3),(0+1+2+3+4),(0+1+2+3+4+5))

注: @ (z /: List(a, b, c))(op) 相当于 op(op(op(z, a), b), c) 简单来说:加法用0,乘法用1 @ (List(a, b, c) :\ z) (op) equals op(a, op(b, op(c, z))) 此处省略一万字。。。。。。。。。。。。。。。

1.8 pattern matching

1.8.1 定义

value match expression,起作用类似if else

def matchTest(x: Int): String = x match { case 1 => "one" case 2 => "two" case _ => "many" //类似case中的default } matchTest(3) // many matchTest(1) // one

1.8.2 与case class

def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = { notification match { case Email(email, _, _) if importantPeopleInfo.contains(email) => "You got an email from special someone!" case SMS(number, _) if importantPeopleInfo.contains(number) => "You got an SMS from special someone!" case other => showNotification(other) // nothing special, delegate to our original showNotification function } } val importantPeopleInfo = Seq("867-5309", "jenny@gmail.com") val someSms = SMS("867-5309", "Are you there?") val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123") val importantEmail = Email("jenny@gmail.com", "Drinks tonight?", "I'm free after 5!") val importantSms = SMS("867-5309", "I'm here! Where are you?") println(showImportantNotification(someSms, importantPeopleInfo)) println(showImportantNotification(someVoiceRecording, importantPeopleInfo)) println(showImportantNotification(importantEmail, importantPeopleInfo)) println(showImportantNotification(importantSms, importantPeopleInfo))

1.8.3 Matching on type only

abstract class Device case class Phone(model: String) extends Device{ def screenOff = "Turning screen off" } case class Computer(model: String) extends Device { def screenSaverOn = "Turning screen saver on..." } def goIdle(device: Device) = device match { case p: Phone => p.screenOff case c: Computer => c.screenSaverOn }

1.8.4 Sealed classes

sealed abstract class Furniture case class Couch() extends Furniture case class Chair() extends Furniture def findPlaceToSit(piece: Furniture): String = piece match { case a: Couch => "Lie on the couch" case b: Chair => "Sit on the chair"//不需要catch all了 }

1.9 Regular Expression Patterns

任何string对象都可以转换成正则表达式:

import scala.util.matching.Regex val numberPattern: Regex = "[0-9]".r//在string后加.r

1.10 For Comprehensions

for (enumerators) yield e

enumerators:可以是generator,或者是filter;

满足要求的就可以将e加入到一个list中

def foo(n: Int, v: Int) = for (i <- 0 until n; j <- i until n if i + j == v) yield (i, j) foo(10, 10) foreach { case (i, j) => print(s"($i, $j) ") // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) }

1.11 Variances

Covariance

如果有class List[+A],则表示该类是covariance。那么如果A是B的subtype,那么List[A]也是List[B]的subtype。

那么就可以在有List[B]的情况下用List[A]替换掉。

Contravariance

如果有class List[-A],那么如果A是B的subtype,那么List[B]是List[A]的subtype。

Invariance

正常情况下,A和B的关系不影响其List之间的关系

type bounds

class PetContainer[P <: Pet](p: P) { def pet: P = p }

[P <: Pet]表示,该类的参数必须是Pet的subtype

1.12 Abstract Types

抽象的type意味着具体的type需要根据实现来定义:

trait Buffer { type T val element: T//element的类型是T }

抽象类extends了traits: SeqBuffer 类的新类型必须是

abstract class SeqBuffer extends Buffer { type U type T <: Seq[U] def length = element.length }

Compound Types

def cloneAndReset(obj: Cloneable with Resetable): Cloneable = { //... }

Polymorphic Methods

与generic class相似,[A]表示某个type

def listOfDuplicates[A](x: A, length: Int): List[A] = { if (length < 1) Nil else x :: listOfDuplicates(x, length - 1) } println(listOfDuplicates[Int](3, 4)) // List(3, 3, 3, 3) println(listOfDuplicates("La", 8)) // List(La, La, La, La, La, La, La, La)

Local Type Inference

类型推断,编译器可以根据表达式进行类型推断,而不用每次都显式地标明。

Polymorphic Methods或者generic class中也可以进行类型推断。

object InferenceTest1 extends App { val x = 1 + 2 * 3 // the type of x is Int val y = x.toString() // the type of y is String def succ(x: Int) = x + 1 // method succ returns Int values }

但是如果返回值是一个递归调用方法,那么类型推断将失效。

以下情况类型推断将会出错:

object InferenceTest4 { var obj = null obj = new Object() }

By-name Parameters与By-value Parameters

def calculate(input: => Int) = input * 37//By-name

By-name在不需要的时候不会evaluated, By-value 在一开始就会且仅会evaluated一次。

2 OO

2.1 Generic Classes

将type作为一个参数的类

类名后加[A],将type A作为参数传入

class Stack[A] {//以下操作都只对type A有用 private var elements: List[A] = Nil def push(x: A) { elements = x :: elements } def peek: A = elements.head def pop(): A = { val currentTop = peek elements = elements.tail currentTop } }

如果type下面还有subtype,那么对于subtype同样适用

class Fruit class Apple extends Fruit class Banana extends Fruit val stack = new Stack[Fruit] val apple = new Apple val banana = new Banana stack.push(apple) stack.push(banana)

2.2 singleton object

用object代替class 现在,sum可以通过import test .Blah.sum导入了

package test object Blah { def sum(l: List[Int]): Int = l.sum }

Scala不能定义静态成员, 所以用Singleton对象来达到同样的目的

import ChecksumAccumulator.calculate object Summer { def main(args: Array[String]) { for (arg <args) println(arg +": "+ calculate(arg)) } }

2.3 继承

超类的私有成员不会被子类继承. 抽象成员, 需要被子类实现(implement) 一般成员, 可用被子类重写(override)

重写方法和属性,两者不能重名,但是可以转换。

参数化属性

class ArrayElement( val contents: Array[String] ) extends Element //实现属性

final成员

加在method前,表示成员函数禁止被任何子类override 加在class前,表示该类禁止被任何子类继承

2.4 Extractor Objects

用法

import scala.util.Random object CustomerID { def apply(name: String) = s"$name--${Random.nextLong}"//apply方法创造一个实例 def unapply(customerID: String): Option[String] = { val name = customerID.split("--").head if (name.nonEmpty) Some(name) else None }//unapply做了反操作,提取出name } val customer1ID = CustomerID("Sukyoung") // 创造了实例 val customer2ID = CustomerID("Suhal") //等价于val name = CustomerID.unapply(customer2ID).get val CustomerID(name) = customer2ID//用于赋值 customer1ID match { case CustomerID(name) => println(name) // prints Sukyoung case _ => println("Could not extract a CustomerID") }

返回值

仅仅是测试,返回 Boolen即可; 返回一个子值,返回Option[T]; 返回多个子值,返回Option[(T1,…,Tn)]。

None:Option的两个子类之一,另一个是Some,用于安全的函数返回值

数据结构

数组

可变的同类对象序列, 适用于OO场景

val greetStrings = new Array[String](3) //greetStrings为val, 但是内部的数组值是可变的 greetStrings(0) = "Hello" //greetStrings.apply(0),所以用() greetStrings(1) = ", " greetStrings(2) = "world!\n" //简单初始化 val numNames = Array("zero", "one", "two")

对应可变的:

val ab = collection.mutable.ArrayBuffer[Int]() ab += (1,3,5,7) ab ++= List(9,11) // ArrayBuffer(1, 3, 5, 7, 9, 11) ab toArray // Array (1, 3, 5, 7, 9, 11) ab clear // ArrayBuffer()

List

List为不可变对象序列

对于List最常用的操作符为::, cons, 把新的elem放到list最前端 :::, 两个list的合并

Nil:长度为0的List

Queues

import scala.collection.immutable.Queue //不可变Queue

val has1 = empty.enqueue(1)//入队 val has123 = has1.enqueue(List(2, 3)) //添加多个元素 val (element, has23) = has123.dequeue //取出头元素,返回两个值, 头元素和剩下的queue

import scala.collection.mutable.Queue //可变Queue

queue += "a" //添加单个 queue ++= List("b", "c") //添加多个 queue.dequeue //取出头元素, 只返回一个值

Stacks

import scala.collection.mutable.Stack

Tuple

tuple和list一样是不可变的, 不同是, list中的elem必须是同一种类型, 但tuple中可以包含不同类型的elem

val pair = (99, "Luftballons") println(pair._1) //从1开始的!

Set和Map

Scala需要兼顾OO和FP, 所以需要提供mutable和immutable版本 默认是Immutable, 如果需要使用mutable版本, 需要在使用前显示的引用

Sorted Set and Map

基于红黑树实现的有序的Set和Map

转载请注明原文地址: https://www.6miu.com/read-47389.html

最新回复(0)