Scala 学习笔记

Scala学习笔记

语言基础

声明变量

// 不指定变量类型
var s=12345          //可变变量
val d=12345          //不可变量,如同java中的final,C/C++中的const

// 指定变量类型
var str: String = "字符串变量"  
val a: Int = 345           //整形变量
var anyType: Any= "任何类型"  // 任何类型变量

// 同时声明多个变量
var msg,note: String = null // 声名String型变量msg和note并初始化为null

常用类型

Byte Char Short Int Long Float Double 和 Boolean

这些基本类型都是类,与Java中不同的是Scala并不刻意区分基本类型和引用类型。

算术和操作符重载

// 基本算数符
var s=1+2*4

由于常用类型都是类,所以Scala中基本操作符都是方法,如:

a 方法 b
a.方法(b)

这里的方法带有两个参数(一个隐式和一个显式的) 如:

1 to 10 
// 等价于
1.to(10)
//Array(1,2,3,4,5,6,7,8,9,10)
注:Scala 中没有 ++ 操作符,需使用 +=1 代替

调用函数和方法

Scala中导入包:import scala.math._ 导入math包中的所有函数,_ 是通配符和Java中的* 一样,Scala中不包含静态方法,但是提供了伴生对象,其作用和Java中的静态方法类似。

apply 方法

"Hello"(1) 实际调用的是 "Hello".apply(1),函数签名为 def apply(n: Int): Char

if 表达式

Scala中if表达式也有返回值,如:

val s = if (x > 0) 1 else -1   // 等价于 int s = x > 0 ? 1:-1

Scala的if表达式的两个分支可以不同类型的返回值,如:

val s = if ( x > 0 ) "x more than 0" else -1

当if表达式中的某一分支没有返回值时,默认返回Unit类型()如:

val s = if (x > 0) "x=" + x   //当x < 0 时 s=()
//等价于
var s = if (x > 0) "x=" + x else ()

输入输出

输出

使用print、println 输出到控制台,和带有C风格的格式化字符串输出printf。

输入

readLine 从控制台读取一行输入,readInt 从控制台读取一个Int,readBoolean从控制台读取一个Bool型数据

循环

Scala 和Java、C++一样拥有 for、while 循环

while循环

while(n > 0){
	r = r * n
	n -= 1
}

for循环

for(i <- 1 to n){
	r = r * i
}

Scala 中的for循环类似Java的for遍历,1 to n 一个取值范围是 $[1,n]$ 的数组

// 表达式 是一个可迭代的对象
for(i <- 列表){
}
for ( i <- 1 until n){
	r = r * i
}

其中 1 until n 是一个取值范围是 $[1,n)$ 的数组

退出循环

Scala 中并没有提供break,或者continue来退出循环,如果需要退出循环可以有以下几种方法:

  1. 使用Boolean型变量
  2. 使用嵌套函数,从当前函数退出
  3. 使用Breaks对象中的break方法。
import scala.util.control.Breaks._
breakable{
	for ( i <- 1 until 10){
	    println(i)
	    if( i == 8 ){
	        break;
	    }
	}
}

for循环推导式

变量 <- 列表 是一个生成器,每个for可以使用多个生成器,使用分号隔开。每个生成器都可以带有一个守卫(以if 开头的Boolean表达式)

for(i <- 1 to 3 ; from = 4 -i; j <- from to 3 ) print(i* 10 + j + " ")
//13 22 23 31 32 33

Scala 中 for循环中的循环体如果以yield开始,则该循环会构建出一个集合,每次迭代生成集合中的一个值。(返回值和第一个迭代器的类型一致)

for(i <- 0 to 1;c <- "Hello") yield (c+i).toChar
//Vector(H, e, l, l, o, I, f, m, m, p)

for(c <- "Hello";i <- 0 to 1) yield (c+i).toChar
//HIeflmlmop

函数

Scala 中除了支持方法外还支持函数。方法能对对象进行操作,而函数不是。在Java中可以使用静态方法来模拟

def abs(x: Double) = if (x>= 0) x else -x
  • 在Scala中除了递归函数需要显示的指定返回类型,其他函数都不需要显示的指定返回类型,可由Scala编译器自动推断。
  • 在Scala 不需使用return,Scala会自动返回最后一个表达式的值
def fac(n: Int): Int = if (n <=0 ) 1 else n * fac(n - 1)

默认参数和带名参数

Scala中支持调用函数时不显示的给出说有参数值,对于这些函数使用默认参数。例如:

def decorate(str: String , left: String = "{", right: String = "}") = left + str + right
decorate("AAA")
// {AAA}

decorate("AAA",">>>")
//>>>AAA}

// 带名参数right,
decorate("AAA",right=">>>")
//{AAA>>>

变长参数

Scala中函数支持变长度的参数列表。定义如下:

def sum(args: Int*) = {
	var result = 0
	for (arg <- args) result += arg
	result
}
sum(1,2,3,4,5,6,7)
// 28

sum(1 to 8: _ *)
//36

表达式: _ * 将数组、列表拆分成函数参数

过程

Scala中如果函数体包含在 {}(花括号)中,但前面没有 = ,那么该函数的返回值类型Unit,这样的函数被称作过程。

def writeToConsole(str: String) {
	print(str)
}
// 等价于

def writeToConsole(str: String): Unit = {
	print(str)
}

懒值

当val被声明为lazy时,它的初始化会推迟到调用时候进行。

lazy val x= scala.io.Source.fromFile("exmaple.txt").mkString

异常

Scala 中异常和Java中相似,使用throw 抛出异常,使用try/catch 捕获异常。与Java中不同的是,Scala不含有受捡异常。Scala 的异常表达式有特殊类型Nothing

var s= if (x > 0) x else throw new IllegalArgumentException("x 不能是负数")

try/finally 语句用于释放资源,无论是否发生异常。

Scala数组操作

Scala数组特点

  • 使用()访问而不是[]
  • 提供初值时不使用new 使用apply方法
  • 长度固定时使用Array长度不定时使用ArrayBuffter
  • 使用for(a <- array) 来进行数组遍历
  • 使用yield 生成新的数组

定长数组

val arr=new Array[Int](10)   //创建一个大小为10存储类型为Int的定长数组
var nums=Array(1,2,3,4,5)    //创建一个大小为5存储类型为Int,并初始化为 1,2,3,4,5的数组

变长数组:数组缓冲

import scala.collection.	mutable.ArrayBuffer
val buf = ArrayBuffer[Int]()    // 声明一个内容为Int的变长数组
//追加元素
buf+=1   
//ArrayBuffer(1)

//追加多个元素
buf+=(2,3,4,5)
//ArrayBuffer(1, 2, 3, 4, 5)

//追加集合
buf++=Array(6,7)
//ArrayBuffer(1, 2, 3, 4, 5, 6, 7)

buf.insert(7,8)   // 在第7位后面插入一个数
//ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8)

buf.insert(8,9,10) //在第8位后插入2个数
//ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

buf.trimEnd(8)  // 移除后8个元素
//ArrayBuffer(1, 2)

buf.toArray         // 生成定长数组
//Array(1, 2)

遍历数组

for(i <- 0 until a.lenght)  
	println(i + ": " + a(i))

for(item <- a)
	println(item)

//倒序访问
for( item <- a.reverse)
	println(item)
for( i <- (0 until a.lenght).reverse)
	println(i + ": " a(i))

// 等距访问 间距为2
for(i <- 0 until (a.lenght,2) 
	println(i + ": " a(i))

多维数组

Scala中的多维数组和Java中一样,是使用数组的数组来实现的。类型为Array[Array[Double]] 要构造这样的数组也可以使用Array.ofDim方法:

val matrix=Array.ofDim[Double](3,4)  //3行4列

matrix(2)(1)  // 使用双括号访问

映射和元组

映射

构建映射,Scala中默认构建的是不可变映射(immutable)

//不可变映射
var map = Map("A"->"AAA","B"->"BBBB")
//Map(A -> AAA, B -> BBBB)

//不能添加新的元素,只能生成新的映射
val map2=map+("2"->"2222")
//Map(A -> AAA, B -> BBBB, 2 -> 2222)

//可变映射
val m=scala.collection.mutable.Map("A"->"AAA","B"->"BBB")
m+=("1"-> "2")
//Map(A -> AAA, 1 -> 2, B -> BBB)

//取值
m("A")
// AAA

//修改值
m("A")="CCC"
// Map(A -> CCC, 1 -> 2, B -> BBB)	

映射迭代

for((k,v) <- 映射) 
	println(k,v)

//只访问key
for(k <- map.keySet)
	println(k)

//只访问值
for(v <- map.values)
	println(v)

Map元素顺序

  1. SortedMap 遍历时按排序后的顺序排序
  2. LinkedHashMap 按插入的顺序排序
  3. Map 按Hash值顺序排序

元组

元组是不同类型的数据集合。例如: (1234,"AAA",22.2) 是一个 Tuple3[Int,String,Double] 的元组。

元组的访问 使用_位置访问元素,如:

var t=(123,"ABC",3.14)
t._1     // 123
t _2     // ABC
t._3     // 3.14

var (first,secode,third)=t       // 通常使用元组来获取元组的值
var (first,_,third)=t   //不需要的值用`_`

元组可用在返回值不只一个的情况

拉链操作

将两个数组一一对应构成元组数组,多出的元素忽略不计

val keys=Array("A","B","C")
val values=Array(1,2,3)
val pair = keys.zip(values)
pair
//Array((A,1), (B,2), (C,3))

要点:

  • 类中 字段自动带有get和set方法
  • 可以自定义get和set方法而替换掉字段的定义,二不必修改类的客户端——统一访问原则
  • 使用@BeanProperty注解来生成JavaBean的getXxx和setXxx方法
  • 每个类都有一个主要构造器,这个构造器和类的定义“交织”在一起,他的参数直接构成类的字段。
  • 辅助构造器是可选的,名字是this

简单类和无参方法

class Counter{
	private var value=0  // 必须初始化
	def increment() = value +=1      //方法默认是公有的
	def current() = value
}

val count=new Counter()   // 创建对象  或 new Counter
count.current         // 调用方法  调用无参方法时可以不带`()`
count.increment()    // 值自增一

声明无参方法时可以不带() 如果声明是没有带() 那么调用时也不能带

Scala中为每个字段都提供get/set方法

class Person{
	var age = 0
}

这个类中声明了一个私有的属性age和公有的get/set,因为age没有声明为private所以get/set 是公有的。在Scala中get/set分别叫做age和age_=。

自定义 get/set 方法

class Person{
	private var privateAge = 0
	def age = privateAge
	def age_= (age: Int) {
		if(age > privateAge) privateAge = age
	}
}

只带有get的字段

当字段声明为val 时,Scala会默认只创建get方法。

对象私有属性

在Scala中不仅支持支持类级私有(只有类的内部方法才能访问私有字段),而且还支持对象级私有(只有对象本身才能够访问的私有字段)。

class Person{
	private var age=0
	private[this] var name = ""   //只能由当前对象访问  不会生成get/set方法
	
	def older(other: Person): Boolean = {
		if(age > other.age){       // 类的方法可以访问私有字段
			true
		}else{
			false
		}
	}
}

Bean属性

Scala会自动生成get/set方法,但是并不满足JavaBeans规范定义的把Java的属性定义为一对getXxx/setXxx方法。许多的Java工具依赖这样的命名习惯。所以Scala提供了注解的方式来生成JavaBeans形式的方法。

import scala.reflect.BeanProperty
class Person{
    var age = 0
    @BeanProperty var name: String = _
}

val person=new Person()
println(person.age)
person.setName("张三")
person.getName

构造器

Scala中包含两种构造器分别是主构造器辅助构造器

辅助构造器

辅助构造器,和Java中的构造器很相似,不同的是:

  1. Scala的辅助构造器的名字是固定的统一是this
  2. 每个辅助构造器都应该调用之前定义的辅助或主构造器
class Person{
    var name:String=_
    var age:Int = 0
    def this(name:String){
        this()
        this.name=name
    }
    
    def this(name:String,age:Int){
        this(name)
        this.age=age
    }
    
}

val person=new Person("Jack",20)
println(person.name,person.age)
//(Jack,20)

主构造器

在类名后面加`private`让主构造器变成私有方法,可用在单例模式。
主构造器参数生成字段/方法
name: String对象私有字段,如果没被使用则没有改字段
val/var name: String私有字段,公有get/set val没有set方法
private val/var name: String私有字段,私有get/set
@BeanProperty val/var name: String私有字段,公有的Scala和JavaBean版的get/set方法
  1. 主构造器参数直接放在类名之后 主构造器中的参数被编译成类的字段
class Person(var name:String,var age:Int){
    override def toString():String = name + " is " + age + " old!"
}

val person=new Person("Jack",20)
person.name
//Jack
  1. 主构造器会执行类定义中的所有语句
import scala.reflect.BeanProperty
class Person{
    println("do with primary constructor")
    var age = 0
    @BeanProperty var name: String = _
    def this(name:String){
        this()
        this.name=name
    }
}

val person=new Person("张三")
println(person.getName())
//do with primary constructor
//张三

嵌套类

在Scala中几乎所有的结构都可以嵌套,函数中嵌套函数,类中嵌套类。

import scala.collection.mutable.ArrayBuffer
class Network{
    class Host(val name:String){
        val contacts=new ArrayBuffer[Host]
    }
    
    private val members=new ArrayBuffer[Host]
    
    def join(name:String): Host = {
        val host=new Host(name)
        members+=host
        host
    }
}

val net1=new Network()
val net2=new Network()

val h1 = new net1.Host("127.0.0.1")
val h2 = new net2.Host("127.0.0.1")
val ht=net1.join("192.168.1.1")
ht.contacts+=h1  // 正确     ht.contacts 类型是net1.Host
ht.contacts+=h2  // 错误

Scala 中每个实列都有自己的Host类,要想实现每个实列的嵌套类都相同,则应该将嵌套类放到伴生对象中。 或者使用类型投影Network#Host,其含义是,任何Network的Host。如:

import scala.collection.mutable.ArrayBuffer
class Network{
    class Host(val name:String){
        val contacts=new ArrayBuffer[Network#Host]
    }
    
    private val members=new ArrayBuffer[Host]
    
    def join(name:String): Host = {
        val host=new Host(name)
        members+=host
        host
    }
}

val net1=new Network()
val net2=new Network()

val h1 = new net1.Host("127.0.0.1")
val h2 = new net2.Host("127.0.0.1")
val ht=net1.join("192.168.1.1")
ht.contacts+=h1
ht.contacts+=h2

对象

  1. 用对象做为单例或者存放工具方法。
  2. 类可以拥有一同名的伴随对象
  3. 对象可以继承类或者特特质(Trait)
  4. 对象的apply方法通常用来构建伴生对象的实列
  5. 如不想定义main方法,可以继承App类
  6. 通过继承Enumeration来实现枚举

单例对象

Scala中没有静态的字段和方法,可以使用object 来实现同样的目的。

class Person(val name:String)

object Person{
    var id: Long=0
    def apply(name:String):Person = {
        id+=1
        new Person(name)
    }
    def show() {
        printf("use apply %d times",id)
    }
}

var m,n,p,k=Person("J")
Person.show()

伴生对象

在Scala中,如果需要实现既有实例方法,又有静态方法,则可以使用伴生对象来实现,伴生对象就是和类同名的对象。


class Person(val name:String){
    val id=Person.uuid()
    override def toString = name + " id is " + id
}

object Person{
    private var id: Long=0
    def uuid():Long = {
        id += 1
        id
    }
}

val p= new Person("Jack")
val p2= new Person("Sam")

枚举类型

Scala中没有提供枚举类型,不过标准库中提供了Enumeration 来实现枚举

object Light extends Enumeration{
    val RED=Value("Red")
    val GREEN=Value("Green")
    val YELLOW=Value("Yellow")
}

包和引入

  • 包可以像内部类一样可以嵌套
  • 包路径不是绝对路径
  • 包声明x.y.z并不自动将中间包x和x.y变为可见
  • 位于文件顶部的不带括号的包声在整个文件范围生效
  • 包对象可以持有函数和变量
  • 引入语句可以导入包、类和对象
  • 引入语句可以出现在任何位置
  • 引入语句可以重命名隐藏特定成员
  • java.lang scalaPredef 总是被引入

Scala 中包可以嵌套,文件绝对路径和包名没有关系。

包的引入

import java.awt.Color 引入java.awt.Color 包,使用通配符_ 可以导入全部对象 import java.awt._

任何地方都可以声明包的导入。

隐藏和重命名

导入几个成员 import java.awt.{Color,Font}

重命名成员 import java.util.{HashMap => JavaHashMap}

隐藏成员 import java.util.{HashMap => _ , _}

继承

  • extends、final 关键字和java中相同
  • 重写方法必须用override
  • 只有主构造器可以调用超类的主构造器
  1. 可以重写字段

超类的主构造器

  • 可以重写字段

继承类

和Java中相同的是final关键字使类不能被继承,和Java中不同的是final 使字段不能被重写

class Employee extends Person{
	var salary = 0.0
}

重写方法

class Employee extends Person{
	var salary = 0.0
	override def toString = "salary is " + salary
}

类型转换

if (p.isInstanceOf[Employee]) {   //检查p是否是Employee类型
	val s = p.asInstanceOf[Employee]    // 将p转换成Employee类型
}

如果p是Employee的一个对象,或是其子类的一对象那么isInstanceOf[Employee] 返回true 否则返回false 如果想测试p是不是Employee的一个对像,则

if (p.getClass == classOf[Employee]){

}

超类构造

Scala中只有主构造器才能调用超类的主构造器

calss Employee(var name:String,var age:Int,var salary:Float) extends Person(name,age){

}

重写字段

Scala中可以使用 val 重写父类的val字段或无参方法。

class Person(val name:String){
    override def toString = getClass.getName + " name is " + name
}

class Employee(cname:String) extends Person(cname){
	override val name = "Secret"
    override val toString = "Secret"
}
val p= new Employee("Jack")
println(p)   //Sercet

在Scala中可以使用val来重写抽象的方法,如:

abstract class Person{
     def id:Int;
}

class Employee extends Person{
    override val id=100
}
// 通过主构造器 重写
class Student(override val id:Int) extends Person

val p =new  Employee()
println(p.id)

重写注意

  1. def 只能重写def
  2. val 只能重写val或不带参数的def
  3. var只能重写抽象的var

匿名子类

class Person(val name:String)
val p=new Person("Jack"){
	def say= "My name is Jack"
}	
println(p.say)  // My name is Jack

抽象类

和Java中一样当类里面包含一个以上的抽象方法时,必须将类声明为abstract 重写抽象方法时可以省略 override

abstract class Person(val name:String){
     def id:Int;
}

class Employee(name:String) extends Person(name){
   override def id = name.hashCode
}

val p =new  Employee("Jack")
println(p.id)  //2300927

抽象字段

在Scala中中为初始化的字段为抽象字段。

abstract class Person{
     val name:String;
     var age:Int;
}

val p =new Person{
    val name = "Jack"
    var age=10
}

println(p.name)  //Jack
println(p.age)  //10

构造顺序和提前定义

在Scala中,如果重写了父类字段,而该字段又在父类的构造其中使用的话,会发生意想不到的问题

class Animal{
	val range:Int=10
	val env:Array[Int]=new Array[Int](range)
}

class Ant extends Animal{
	override val range:Int = 2
}

val a=new Ant()
a.env
// Array()   // 错误输出

改正方式:

class Ant extends {	override val range:Int = 2 } with Animal{

}

val a=new Ant()
a.env
// Array(0,0) 
注:经测试重写父类中使用该字段的方法和字段也可以达到预期结果,但是,尽量避免未初始化字段被使用
class Ant extends Animal{
	override val range:Int = 2
    override val env:Array[Int]=new Array[Int](range)
}
val a=new Ant()
a.env
// Array(0,0)

Scala对象继承树

Scala对象相等性

在Scala中,AnyRef的eq方法检查两个引用是否指向同一个对象。AnyRef的equals方法调用eq方法,当实现一个类是应当重写相应的eq方法。 当重写equals方法时,应当重写hashCode方法。确保当两个对象不相同时,其hashCode也不同,否则会有 也不同,否则会有意想不到的问题发生。

文件和正则表达式

  • Source.fromFile().getLines.toArray 输出文件所有的行
  • Source.fromFile().getLines.mkString 以字符串形式输出文件内容
  • 使用Java的PrintWriter来写入文本文件
  • "正则".r 是一个Regex对象

集合操作

map

var list=List(1,2,3,4)
list.map(_*2)
// List(2,4,6,8)

def ulcase(s:String) = Vector(s.toUpperCase,s.toLowerCase)
val list=List("Apple","Moon","Test")
list.map(ulcase)
//List(Vector(APPLE, apple), Vector(MOON, moon), Vector(TEST, test))

flatMap

def ulcase(s:String) = Vector(s.toUpperCase,s.toLowerCase)
val list=List("Apple","Moon","Test")
list.flatMap(ulcase)
//List(APPLE, apple, MOON, moon, TEST, test)

License

转载于:https://my.oschina.net/albert2011/blog/800324

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值