1.scala简介
2004年,martin ordersky发明,javac的编译器,后来spark,kafka应用广泛,twitter应用推广。它具备面向对象和函数式编程的特点。
官网:www.scala-lang.org,最近版本2.12.5,我们用的是2.10.4
2.环境安装
1) windows
a) 安装jdk-7u55-windows-x64.exe
b) 安装scala-2.10.4.msi
安装完以上两步,不用做任何修改。
测试:在控制台下c:/>scala
c) 安装eclipse-java-juno-SR2-win32-x86_64.zip 解压缩即可
d) 安装eclipse的scala插件update-site.zip
解压会有两个目录,features和plugins,分别把内容放到eclispe对应的目录下。
e) 重启eclipse
提示"Upgrade of scala...",点yes
提示框"setup Diagnostic",把Enable JDT weaving...选上
根据提示重启
2) linux
3.第一个程序
1) 交互式编程
C:\Users\Administrator>scala
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_5
Type in expressions to have them evaluated.
Type :help for more information.
scala> 1+1
res0: Int = 2
2) 脚本形式:通过创建文件来在eclipse/idea中执行代码
object Test {
def main(args: Array[String] ) {
println("Hello world")
}
}
注1:上面这个是单例对象,这个里面只能存放静态的东西
注2:自动导入2个包
java.lang._
scala._
注3:语句最后一行的分号不推荐写
4.基础语法
1) 变量和常量
val(常量)和var(变量) 尽可能的用val
(1) 变量
格式:
var 变量名 [:数据类型] = 值
例:var b :Int = 1
var c = 2 //类型自动推断
(2) 常量
格式:
val 常量名 [:数据类型] = 值
例:
val b :Int = 1
val b = 1
b = 2 //报错,常量不能修改
(3) 常量可以用lazy修饰(了解)
lazy val b :Int = 1 //b用到的时候再赋值
2) 数据类型
(1)数据类型
序号 数据类型 说明
1 Byte 8位有符号值,范围从-128至127
2 Short 16位有符号值,范围从-32768至32767
3 Int 32位有符号值,范围从-2147483648至2147483647
4 Long 64位有符号值,范围从-9223372036854775808至9223372036854775807
5 Float 32位IEEE 754单精度浮点值
6 Double 64位IEEE 754双精度浮点值
7 Char 16位无符号Unicode字符。范围从U+0000到U+FFFF
8 String 一个Char类型序列
9 Boolean 文字值true或文字值false
10 Unit 对应于无值,等价于void类型,只有一个对象叫()
11 Null 只有一个对象叫null
12 Nothing 在Scala中处于最底层,比如创建数组时不指定类型,就是Noting。抽象概念
13 Any 任何类型的超类型; 任何对象的类型为Any
14 AnyRef 任何引用类型的超类型
(2)层次结构
Scala.Any
-AnyVal(值)
-Int,Double等,Unit。
-AnyRef(引用)
-List,Set,Map,Seq,Iterable
-java.lang.String
-Null
(3)重点类型:元组
格式:(元素1, 元素2, ....)
访问:变量._N 其中N是元组元素的索引,从1开始
例:var t = ("a", false, 1) //t的类型是scala.Tuple3
var value = t._1 //"a"
var m,n,(x,y,z) = ("a", false, 1)
m :("a", false, 1)
n :("a", false, 1)
x : 'a'
y : false:
z : 1
(4)重点类型:字符串
i) 用的是java.lang.String,但是有时候根据需要,会隐式转换到其它类型,比如调用reverse/sorted/sortWith/drop/slice等方法,这些方法定义在IndexedSeqOptimized中
ii)多行字符串表示,开始和结束用
(4)重点类型:字符串
i)用的是java.lang.String,但是有时候根据需要,会隐式转换到其它类型,比如调用reverse/sorted/sortWith/drop/slice等方法,这些方法定义在IndexedSeqOptimized中
ii)多行字符串表示,开始和结束用
(5)了解:符号类型
符号字面量: '标识符,是scala.Symbol的实例,像模式匹配,类型判断会比较常用。
var flag = 'start
if (flag == 'start) println(1) else println(2)
3) 运算符:scala没有运算符,它运算符全部封装成了方法。
算术:+ - * / %
比较: == != > < >= <=
逻辑:&& || !
赋值:= += -= *= /* %=
位:& | ~ ^ >> << >>> #*/
注:上面都是方法。例 1+2相当于1.+(2),其中+是方法,2是参数
4) 控制语句
(1) if,if...else...,if...else if...else...
(2) scala中的if可以作为表达式用
var x = if("hello"=="hell") 1 else 0
(3) switch被模式匹配替换
5) 循环语句
(1)while,do..while和for都有,while和do..while很像,但是for差别很大。
(2)for循环格式不同
for(变量 <- 集合 if 条件判断1;if 条件判断2...) {
所有条件判断都满足才执行
}
(3)没有break和continue,用两种方法可以代替
i) 用for循环的条件判断
ii)方法2:非写break,要做以下两步
//1) 引入一个包scala.util.control.Breaks._
//2) 代码块用breakable修饰
break
breakable {
for(i<-0 to 10) {
println(i)
if (i == 5) {break;}
}
}
//0 1 2 3 4 5
continue
for(i<-0 to 10) {
breakable {
if (i == 3 || i==6) {
break;
}
println(i)
}
}
//0 1 2 4 5 7 8 9 10
(5) 集合框架(Array,List,Set,Map)
在scala中,数组Array归到集合的范畴
包scala.collection,下面有两个分支immutable(不可改变的,默认)和mutable(可变的)
(1) 层次结构
Traversable
-Iterable
(immutable不可改变的长度,默认)
-Set 无序的集合,没有重复元素
HashSet,TreeSt 其中HashSet内部定义了别名Set
-Map 键值对,映射
HashMap,TreeMap 其中HashMap内部定义了别名Map
-Seq 序列。有先后顺序的元素集合
-IndexedSeq
Array:有索引的元素序列,因此可以通过下标访问
Vector,String,Range
-LinearSeq
List:线性的元素序列。访问第1个元素,head方法,最后1个tail方法
Queue,Stack
(mutable可变的长度)
-Set
HashSet 其中HashSet内部定义了别名Set
-Map
HashMap 其中HashMap内部定义了别名Map
-Seq
-IndexedSeq
ArraySeq,StringBuilder
-Buffer
ArrayBuffer,ListBuffer
-LinearSeq
LinkedList,Queue
Stack
重点记忆几个
不可变 可变
Array ArrayBuffer
List ListBuffer
Map Map
Set Set
(2) 数组(非常重要):数据类型相同的元素,按照一定顺序排序的集合。
不可变数组 scala.Array
可变长数组 scala.collection.mutable.ArrayBuffer
Array和ArrayBuffer
1.Array创建
var 变量名 = new Array[类型](长度) //var arr = new Array[Int](10)
var 变量名 = Array(元素1,元素2,...) //var arr = Array(1,3,5,7),实际上是调用Array的方法apply
取值:变量名(下标)
2.ArrayBuffer创建
var 变量名 = new ArrayBuffer[类型]() //var arr = new ArrayBuffer[Int]()
var 变量名 = ArrayBuffer(元素1,元素2,...) //var arr = ArrayBuffer(1,3,5,7)
3.共同方法
sum 求和
max 最大值
min 最小值
mkString(分隔符) 例:Array(1,3,5,7).mkString("|") //结果是1|3|5|7|9
sorted 排序(从小到大)
sortBy
sortWith 自定义排序
reverse 翻转
toArray ArrayBuffer转成Array
toBuffer Array转成ArrayBuffer
toMap:如果Array内部元素是对偶元组,可以转成map
toList:转成list
4.ArrayBuffer的独有方法
+= 追加元素
++= 追加集合
trimEnd(n):删除末尾n个元素
insert(index,value1,value2,..valueN):第index的位置,插入value1,value2,...valueN)
remove(index,n):第index个位置删除n个元素
clear():清空
5.遍历
i) 通过下标
for (i <- 0 to 数组.length-1) {
println(i)) //i表示数组的下标
}
ii)直接取值
for (e <- 数组) {
println(e)
}
6.使用yield创建新数组
var arr = for (i <- 数组) yield i * 2
7.多维数组
var arr = Array(Array(元素1...),Array(元素,...)..)
遍历
for (i <- arr) {
for (j <- i) {
println(j)
}
}
(3) List(不可变)/ListBuffer(可变):List 和java.util.List不同,一旦创建不可以改变。一般做数据分析用List
1.List定义
//创建
var list1 = List("aa","bb","ccc") //调用apply方法
var list2 = List.apply("aa","bb","ccc"))
//右操作符:当方法名以:结尾时,为右操作符,先做右边
//::右操作符,拼接元素,
var list3 = "aa"::"bb"::"cc"::Nil //Nil是空集合,先做"cc"::Nil,其中::是Nil的方法
var list4 = list1:::"dd"::Nil //:::拼接集合
2.常用方法
//查
head() 第一个元素
take(n) 取前n个元素
tail() 除了第1个的全部元素
init() 除了最后一个的全部元素
//增
:+和+: 前者是尾部追加元素,后者是在头部追加元素,和::很像
+=或append 追加元素(ListBuffer)
++= 追加数组/列表(ListBuffer)
:: 追加元素(List)
::: 追加数组/列表(List)
//删
drop(n) 丢弃前n个元素
//改
updated(index,value):用value地替换第index的位置的元素
//其他
isEmpty是否为空
reverse 翻转
splitAt(m):把列表前m个做成一组,后面是一组
flatten:以参数的列表作为参数,把所有的元素连接到一起
//toArray 转成Array
toArray 转成Array
zip:两个List内部的元素合并,返回结果是List,元素是对偶元组
grouped(n):每n个元素分成1组,返回是迭代器Iterator,可以再用toList转成list类型
3.ListBuffer
import scala.collection.mutable.ListBuffer
var list5 = ListBuffer("111","222","333")
(4) Set:不存在重复元素的集合
如果用可变的,需要引入import scala.collection.mutable.Set
//定义
var s = Set(3.0, 1.0)
//追加元素
s += 2.0 //s = s + 2.0
s += 2.0 //元素重复,加不进去
(5) Map
//定义
var m1 = Map(("zhang3",20),("li4",23),("wang5",21)) //使用元组
var m2 = Map(("zhang3"->20),("li4"->23),("wang5"->21))
var m3 = Map[String,Int]() //定义空map,得用可变的才有意义。
//添加和更新
var m = scala.collection.mutable.Map(("zhang3", 20), ("li4", 23), ("wang5", 21))
m += (("zhao6", 22)) //
m = m.updated("zheng7",19) //和上面方法一样
m.put("wu", 22)
m ++= Map(("a" -> 1), ("b" -> 2))
//删除
m -= (("zhao6", 22))
m.clear() 清空
//查询
m(key):通过key获取value
m.get(key) :通过key获取value,返回类型Option
m.getOrElse(key,获取不到设置的值)
//其它
size:元素个数
contains(key):是否包含key,返回布尔类型
keys 返回迭代器
keySet 返回所有的key组成的Set集合
values 返回迭代器,所有value组成的集合
mapValues(函数)
foreach(函数)
//遍历
(6) Array和List都是不可变元素,有什么区别?
5.函数
Scala中函数和方法,在类中定义的函数就是方法。
1) 函数声明
def 函数名([参数列表]):[返回类型] = {
函数体
return [表达式]
}
注1:如果函数没有返回值,可以返回Unit,也可以不写
注2:方法的返回类型可以不写,系统自动推导,但是递归必须写。
注3:函数体的大括号只有一行,可以不写
例:
def add(a:Int,b:Int):Int = {
var sum : Int = 0
sum = a + b
return sum
}
2) 函数调用
格式:
方法之间调用:函数名([实参列表]) 例:add(3,5)
对象之间调用:实例名.函数名([实参列表]) 例:s.add(3,5)
3) 值函数
(1)定义
var 变量名:[输入类型=>输出类型] = {
(参数列表)=>{函数体}
}
其中=>是函数映射符
在参数列表后不能写返回值类型
最后一句不能写return
Lambda表达式(匿名函数,函数字面量):形如(参数列表)=>{函数体}就叫Lambda表达式
4) 高阶函数
1)
定义:本身是函数,参数也是函数或返回值是函数
例1:
def m(f:(Double)=>Double):Double = {f(100)}
def sqrt(x:Double)=Math.sqrt(x)
调用m(sqrt) 结果是10.0
2) 常见高阶函数
集合中常见的高阶函数
(1)map
def map[B, That](f: A => B): That
把函数应用于集合中的每一个元素,把返回由返回值组成的一个集合。
例:
Array("spark","hive","haddop").map((x:String)=>x.length)
Array("spark","hive","haddop").map(x=>x.length)
Array("spark","hive","haddop").map(_.length) // _占位符,代表一个元素
Array("spark","hive","haddop").map(x=>println(x))
Array("spark","hive","haddop").map(println _)
Array("spark","hive","haddop").map(println) //如果只有一个参数,可以不写
(2) flatMap
把函数应用于集合中的每一个元素,先做map,得到的结果flatten,生成新的集合。
例:
var list = List("a,b,c","d,e,f")
var list2 = list.map(x=>x.split(",").toList) //List(List(a, b, c), List(d, e, f))
list2.flatten //List(a,b,c,d,e,f)
上面两步可以写成一步,相当于map+flattern
list.flatMap(x=>x.split(",").toList) //简写:list.flatMap(_.split(",").toList)
问:map和flatMap有什么区别?
(3)filter
参数的函数表示对集合的每个元素过滤,参数函数结果是boolean,最后的返回值还是List
List(1,2,3,4).filter(x=>x>2) //List(3,4)
Array(1,2,3,4,5,6,7,8).filter(x=>x%2==0) //取偶数 2,4,6,8
Range(1,100).filter(_%2==0) //1到100的偶数
Array(1,2,3,4,5,6,7,8).filter(x=>x%2==0).map(_*10)//20,40,60,80
注:filterNot和filter相反
(4)reduce/reduceLeft/reduceRight
返回1个值。参数函数要求是二元运算符,作用于集合上,每次返回的结果作为新的输入。
Array(1,3,5,7).reduce((x,y)=>{println(x + ":" +y);x+y})
Array(1,3,5,7).reduce(_+_) //第1个_代表第1个参数,第2个_代表第2个参数
Array(1,3,5,7).reduceLeft((x,y)=>{println(x + ":" +y);x+y})
Array(1,3,5,7).reduceRight((x,y)=>{println(x + ":" +y);x+y})
注:reduce没有特别的顺序,reduceLeft从左到右计算,reduceRigth从右到左计算
(5)fold/foldLeft/foldRight
和reduce很像,只是增加了一个初始值,但是做并行时要注意。
Array(1,3,5,7).fold(4)(_+_) //20
List(2.0,3.0).fold(4.0)(Math.pow) //4096.0
(6) par
把计算转换为并行化,把reduce拆分多个任务
Array(1,3,5,7,8).par.fold(4)(_+_)
Range(1,10).par.map(println)
(7) groupBy
对列表进行分组,返回结果是map
var data = List(("zhang3","male"),("li4","female"),("wang5","male"))
data.groupBy(x=>x._2) //Map(male-> List((zhang3,male), (wang5,male)), female -> List((li4,female)))
多介绍一个grouped(n):每n个分成一组
例:List(1,3,5,7,9).grouped(2).toList //List(List(1, 3), List(5, 7), List(9))
(8)partition
把列表分成两部分,第一个为满足条件的,第二部为不满足条件的
List(1,3,5,7,9).partition(x=>x>4) //分成两组:(List(5, 7, 9),List(1, 3))
(9)diff差集,union并集并保留重复,intersect交集
var n1 = List(1,2,3)
var n2 = List(2,3,4)
var n3 = n1.diff(n2) //也可以写n1 diff n2 ,结果:List(1)
var n4 = n2.diff(n1) //List(4)
var n5 = n1 union n2 //List(1 2 3 2 3 4)
var n6 = n1 intersect n2 //List(2,3)
(10)distinct
去掉重复元素
List(1,2,3,2,3,4).distinct // List(1, 2, 3, 4)
(11)mapValues(函数)
还是map功能,只是处理的值是value,最终结果是map
Map(("zhang3",20),("li4",23),("wang5",21)).mapValues(_*2)//结果Map(zhang3 -> 40,li4 -> 46,wang5 -> 42)
(12)foreach(函数)
和map一样,区别是它没有返回值,通常用在遍历
(13)sorted 排序和sortBy和sortWith
sorted 排序,最简单,从小到大
List(2,1,3,5,6,4).sorted //1 2 3 4 5 6
sortBy:按照第几列排
List(("a",3),("b",1),("c",2)).sortBy(_._2) //按照第2列排
sortWith:自定义排序
List(2,1,3,5,6,4).sortWith((x,y)=>x>y) //从大到小
3)下划线_的用法
(1) 让匿名函数更简洁,可以用下划线_当做一个占位符。
(2) java中导入包下的所有类用*,在scala中用_
例:
Java: import java.util.*
scala: import java.util._
(3) 下划线_可以作为部分应用函数
4) 函数柯里化
柯里化(curring) 把接收多个参数的函数编程接收一个单一参数
例1:不是柯里化,但是和柯里化很像
def foo(factor : Int) = (x:Double)=>factor * x;
调用:
var f = foo(10)
f(50)
也可以直接写:foo(10)(50)
例2:柯里化
def foo(factor : Int)(x:Double) = factor * x
调用:foo(10)(50)
注:柯里化函数不是高阶函数,不能这么调用var f = foo(10)
5)部分应用函数(Partial Applied Function)
部分应用函数, 是指一个函数有N个参数, 而我们为其提供少于N个参数, 那就得到了一个部分应用函数.,柯里化函数和普通函数都有部分应用函数
6)偏函数(Partial Function)
(1)简介
引子:
scala> List(1,2,3) map {case i:Int=>i+1}
scala> List(1,2,3) map {(i:Int)=>i+1}
上面两个结果是一样的。
偏函数:上面例子中map后面的被包在花括号内的代码,也就是没有 match的一组case语句,就叫偏函数。
偏函数类型是:PartialFunction[A,B],A的参数类型,B是返回结果类型。平常我们都是直接用。
例1:
val foo : PartialFunction[Int,String] = { //类型PartialFunction[Int,String]可以省略
case y if y % 2 == 0 => y + " 是偶数"
}
println(foo(10)) //10 is Even
println(foo(11)) //抛出错误: scala.MatchError
----反编译:相当于foo是一个对象,调用对象的 apply方法
private final PartialFunction foo = new PartialFunction{
public final Object apply(int i){
Object obj;
if (i % 2 == 0)
obj = y + " 是偶数"
else
obj = i;
return obj;
}
}
foo.apply(10)
(2)orElse:可以把多个偏函数结合起来,效果类似case语句。
val or1 = {case 1 => "One"}
val or2 = {case 2 => "Two"}
val or_ = {case _ => "Other"}
val or = or1 orElse or2 orElse or_ //使用orElse将多个偏结合起来
//测试:
scala> or(1) // one
scala> or(20) //other
(3) andThen: 相当于方法的连续调用,比如g(f(x))。
val a = {case cs if cs == 1 => "One"}
val b = {case cs if cs eq "One" => "The num is 1"}
val c = a andThen b
//测试
num(1) //The num is 1
(4) 普通函数的偏应用函数定义
比如我先定义一个函数:
def sum(a:Int,b:Int,c:Int) = a + b + c;
那么就可以从这个函数衍生出一个偏应用函数是这样的:
def foo = sum(1, _:Int, _:Int)
调用foo(2,3), 相当于调用sum(1,2,3) 两个_分别对应函数sum对应位置的参数.
用处:当你在代码中需要多次调用一个函数, 而其中的某个参数又总是一样的时候, 使用这个可以少敲一些代码
(5)柯里化函数中的偏应用函数定义
def sum(a:Int)(b:Int)(c:Int) = a + b + c; // (a: Int)(b: Int)(c: Int)Int
val result = sum _ //柯里化赋值,结果是部分应用函数 Int => (Int => (Int => Int)) = <function1>
sum(1)(2)(3) //6
result(1)(2)(3) //6
result(1)(_: Int)(3) //部分应用函数:Int => Int = <function1>
2018/4/5
6.面向对象
1) 类和对象
(1) 简介
scala中的类和java中的类一样,都是通过class定义
class Person { //不写public,默认public
//var name:String; //报错,变量必须要被初始化,而java不需要被初始化,系统会自动赋初值
var name :String = null //正确
var age :Int = _ //正确,不知道赋上面值时,用_占位符
}
(2)创建对象
var p1 = new Person()
var p2 = new Person //没有参数,可以省略()
(3) 类成员访问
p1.name = "zhang3" //设置,底层调用的是p1.name_=("zhang3"),其中_=是方法
println(p1.name) //取值
非要用getter和setter,属性前加入 @BeanProperty(导入包:scala.beans._)
(4) 单例对象
scala中不支持静态成员的语法,通过单例对象实现
object Test {
private var id:Int = 0
def m() ={
id += 1
id
}
}
测试:
scala>Test.m //1
scala>Test.m //2
(5) 伴生对象和伴生类
单例对象和类如果同名的话,单例对象也叫伴生对象。
例:
class A {}
object A {
}
伴生对象和伴生类可以互相访问,即使是private也可以访问,但是private[this]不能访问
一个文件可以有多个类
(6) apply方法
object Dog {
def apply():Unit = {
println(1)
}
def apply(name:String):Unit = {
println(2)
}
def main(args: Array[String]) {
var d1 = Dog() //调用Apply()方法
var d2 = Dog("hello") //调用Apply(String)方法
}
}
2) 主构造方法
(1)定义
每个类都有主构造方法,参数放在类名后,构造方法和类交织在一起。
class Student(var name:String, var age:Int)
反编译代码:
public class Student {
private String name;
private int age;
public Student(String name, int age) {
println("hello:" + name)
this.name = name;
this.age = age;
}
//getter和setter
//name()和name_$eq(String)
//age()和age_$eq(int)
@Override
public String toString() {
return name + ":" + age
}
}
Student s = new Student();
System.out.println(s) //打印s,就是打印s.toString()
例:
class Student(var name:String, var age:Int) {
println("hello") //加在主构造方法中
override def toString = name + ":" +age
}
var s = new Student()
println(s)
(2) 带有默认参数的主构造方法
构造方法可以带有默认参数。
class Student(var name:String, var age:Int, val gender:String="female") {
println("执行主构造方法")
}
var s1 = new Student("zhang3", 18, "male") //正确
var s2 = new Student("li4", 18) //正确
s2.name = "wang5"
s2.gender = "male"
(3) 私有构造方法
class Student private(var name:String, var age:Int)
var s = new Student("li4", 18)//报错
3) 辅助构造方法
(1) 非主构造方法,方法名用this
(2) 非主构造方法,也可以有默认值
class Dog(val id:String) {
var name :String = "大黄"
def this(id:String,name:String) {
this(id)
this.name = name
}
override def toString = name + ":" + id
}
//测试
var d1 = new Dog("1111")
var d2 = new Dog("2222","二黄")
println(d1)
println(d2)
4) 继承和多态
(1) 继承用extends
class Person(var name:String, var age:Int)
//没有写var,表示继承父类的属性
class Student(name:String,age:Int, var no:String) extends Person(name,age)
//测试
new Person("tom", 18)
new Student("jack",19,"123")
Student从父类继承name和age,所以前面不要加变量var或者val,no是新加的元素,所以需要var
(2) 多态
多种形态,动态绑定,在执行期间确定引用对象的实际类型,根据其实际类型调用相应的方法,也就是
子类可以赋值给父类。
· class Human(var name:String, var age:Int) {
def walk():Unit = {
println("walk in Human")
}
}
class Teacher(name:String,age:Int) extends Human(name,age) {
override def walk():Unit = {
println("walk in Teacher")
}
}
class Doctor(name:String,age:Int) extends Human(name,age) {
override def walk():Unit = {
println("walk in Doctor")
}
}
//测试
var x:Human = new Teacher("tom", 18)
var y:Human = new Doctor("jack", 19)
x.walk //walk in Teacher
y.walk //walk in Doctor
5) 成员访问控制
private 类内+伴生对象
protected 同包+子类
不写:默认是public,都可以访问
特别之处
(1) private[this] 只能类内访问
class Person {
private[this] var name:String = "zhang3" //只能类内
private var age: Int = 18 //类内+伴生对象
}
object Person {
def main(args:Array[String]) {
var p = new Person
print(p.age) //18
//print(p.name) //报错
}
}
(2) 主构造方法也可以加入成员访问控制
class Person(private var name:String, protected var age:Int)
6) 抽象类
不能能被实例化的类。可以有抽象方法,非抽象方法,成员变量。但是scala增加了抽象成员变量。
abstract class Person {
var name:String //抽象成员变量
def walk //抽象方法
}
//子类如果继承,必须要实现抽象变量和抽象方法,如果不想实现,子类也需要是抽象类
class Student extends Person {
override var name:String = _ //覆盖父类的抽象变量
override def walk():Unit = {println("walk in Student")} //覆盖父亲的抽象方法
}
7) 内部类和内部对象
定义在对象或者类内部的类
object Ex extends App {
class Student(var name:String, var age:Int) {
//内部类
class Grade(var name:String)
//内部对象
object Utils {
def print(name:String) = println(name)
}
}
object Student {
//内部类
class Printer {
def print(name:String) = println(name)
}
}
var s = new Student("tom", 18)
//var grade = new Student.Grade("xxx") //此处出错,怎么纠正?
s.Utils.print("student:util.print(String)")
new Student.Printer().print("Student.Printer().print(String)")
}
8) 匿名类
没有名字的类,一般用在某个类只在代码出现一次
abstract class Person(var name:String, var age:Int) {
def print():Unit
}
//调用
//var p = new Person("tom", 18) //错误,抽象类不能new,也就是不能创建实例
var p = new Person("tom", 18) {
def print():Unit = println("hello")
}
p.print();
9) trait(特质),其实就是接口,相当于jdk1.8的接口,比jdk1.8以下版本的接口多了非抽象的变量和方法
(1) scala没有提供接口interface,而用trait关键字,其实和接口等价。
使用方法:extends或with,第一个实现的特质用extends,之后的用with
它和抽象基本一样,可有抽象成员,抽象方法,具体成员和具体方法
例:
trait Closeable {
def close():Unit
}
trait Clonable {
def open():Unit
}
//第一个接口用extends,后面的用with
class File(var name:String) extends Closeable with Clonable{
def close():Unit = println("file close")
def open():Unit = println("file open")
}
//写一个全的trait
trait Book {
var a : Long = _ //具体成员
var b : Long //抽象成员
def add(x:Int) //抽象方法
def add2(x:Int):Int = x+2 //具体方法
}
var b = new Book(){ //定义匿名内部类,只需要实现抽象方法和抽象成员变量即可
override var b : Long = 1
def add(x:Int) = println(x)
}
b.add(6)
println(b.add2(5))
10) 自身类型(self type)
trait A {
val a = 1
}
trait B {
this:A=> //引入接口A,相当于把A的东西全放进来
var b = 2
def xx() {
println(this.a + ":" + this.b)
}
}
class C extends B with A
//调用
new C().xx()
//还有一种特殊用法,this定义别名
class D {
self=> //为this定义别名self,也可以是其他名字
var x = 2
def foo = self.x + this.x
}
//常见用法,用在内部类引用外部类的变量
class OuterClass {
xx=> //为this定义别名
var value = "here"
class InnerClass {
println(xx.value)
}
}
7.包
1) 包定义
(1) 第一种定义方式,和java很像,
package cn.cda.day1
(2) 第二种定义方式
package cn{
package cda {
package day1 {
class Teacher {
}
}
package day2 {
class Student
}
}
}
以上方式不推荐
2) 作用域
scala中有private,proteced和默认(public)外,还有private[x]和protected[x],其中x可以是包/类/单例对象
3) 包对象
通过package关键字声明的对象为保对象,主要用于对常量和工具函数的封装,通过包名引用。
package object Math {
val PI = 3.14
}
class Test {
def main(args:Array[String]) {
m(3) //3.14*9=28.62
}
def m(r:Double) = Math.PI * r * r
}
4) import
导入包下的所有的类和对象,还可以引入重命名和类隐藏等功能。
1) 隐式导入
默认导入的包,通过下面命令查看
scala>:import
2) 引入重命名
import java.util.{HashMap=>JavaHashMap}
import scala.collection.immutable.HashMap
var map = new JavaHashMap
3) 类隐藏
引入java.util.下的所有类,但是不想要HashMap
import java.util.{HashMap=>_,_} //隐藏HashMap
import scala.collection.immutable.HashMap
var map = new HashMap
8 模式匹配
1) 简介
模式匹配类似switch语言,简单用法就是替代多层的if else语句和类型检查和快速匹配。
它使用了关键字match来替换switch,一旦匹配到了,后面不再执行,所以不用break语句。
scala有一个十分强大的模式匹配机制,可以用到很多场合。如switch,类型检查等,
格式:
case 变量: 类型=> 代码
例:java和scala进行比较
--java
for (int i = 1; i < 5 ; i++) {
switch (i) {
case 1:System.out.println(1);break;
case 2:System.out.println(2);//不带break会陷入其它分支,同时输出2,3
case 3:System.out.println(3);break;
default:System.out.println("default");
//case语句不能接表达式
//case i%5==0:System.out.println("10")
}
}
--scala
for (i <- 1 to 5) {
i match {
case 1 => println(1) //不需要写break,
case 2 => println(2)
case 3 => println(3)
case _ => println("default")
}
}
//上面的case语句中,还可以加入表达式
for (i <- 1 to 5) {
i match {
case 1 => println(1) //不需要写break,
case x if (x%2 == 0) => println(s"$x 是偶数") //加入表达式 ,此时的x是形参,i是实参
case _ => //这句话表示其它任何情况都不进行操作
}
}
//还可以作为函数体
def m(x:Int) = x match {
case 5 => "整数5"
case y if (y % 2 == 0) => "能被2整除" //注意y认为是形参
case _ => "其它整数"
}
println(m(5)
//反编译:
public String m(int x){
switch (x){
default: tmpTernaryOp = "其它整数"; break;
}
return x % 2 == 0 ? "能被2整除" : "整数5";"
}
2) 模式匹配的7大类型
(1)常量模式
case后面接的全部是常量
例:for (i <- 1 to 5) {
i match {
case 1 => println(1) //不需要写break,
case 2 => println(2)
case 3 => println(3)
case _ => println("default")//可以写成 case x
}
}
(2)变量模式
case后面接的是变量
例:
def m(i:Int) = i match {
case x if (x % 2 == 0) => "能被2整除" //这个顺序是有关系的
case x if (x % 3 == 0) => "能被3整除"
case x => "能被2或3意外的数" // 等价于 case _
}
//调用
println(m(5))
println(m(4))
println(m(3))
(3)构造方法模式
与创建对象相反,见下
例1
case class Dog(val name:String, val age:Int)
def m(x:AnyRef) = x match {
case Dog(name,age)=>println(name+":"+age)// 构造函数模式,与创建对象相反,对对象进行析构,调用unapply方法,普通类没有,case类系统会生成。
case _=>
}
//测试
val dog = Dog("Pet", 2) //调用apply创建对象
m(dog)
例2:对部分变量析构
case class Dog(val name:String, val age:Int)
def m(x:AnyRef) = x match {
case Dog(_,age)=>println(age) //表示匹配这个成员域,但是不需要使用nam的值
case _=>
}
(4)序列模式
用于匹配Seq集合的,用法和上面类似,但是可以使用_*来匹配多个元素,并且只能放在模式内的最后
例:
def m(x:AnyRef) = x match {
case Array(a,b)=>println("1")
case Array(a,_,b,_*)=>println("2") //_匹配第2个元素,_*匹配任意多,只能放模式最后
case _=>
}
//调用
m(Array(1,2,3,4))
(5)元组模式
和上面用法相似,但是不能使用_*,_*只能在序列模式中使用。
例:
def m(x:AnyRef) = x match {
case (a,b)=>println("1")
case (a,_,b,_)=>println("2")
//case (a,_*)//元素模式不能使用_*
case _=>
}
var t = (1,2,3,4)
m(t)
(6)类型模式
匹配变量的类型
class A
class B
class C extends A
def m(x:AnyRef) = x match {
case y:String=>println(1)
case y:B=>println(2)
case y:A=>println(3)
case _=>println(4)
}
//测试
val b = new B
val c = new C
m("hello")
m(b)
m(c)
反编译源代码
public void m(Object x)
{
if ((x instanceof String)){
this.println(1);
}
else if ((x instanceof B)){
this.println(2);
}
else if ((x instanceof A)){
this.println(3);
}
else {
this.println(4);
}
}
(7)变量绑定模式
如果想要的不是析构对象,而且返回匹配模式的对象,很常用
例1
case class Dog(val name:String,val age:Int){
override def toString=name+":"+age
}
def m(x:AnyRef) = x match {
//Dog(_,_)用于匹配输入对象中包括两个成员变量的Dog对象,匹配成功后把对象赋值给d
case d@Dog(_,_) => println("变量绑定模式返回的变量值:" + d)
case _=>
}
// 测试
val dog = new Dog("Pet", 2)
m(dog)
反编译源代码:
public void m(Object x) {
if ((x instanceof Dog)){
Dog d = (Dog)x;
this.println("变量绑定模式返回的变量值:" + d)
}
else {
}
}
例2:更常见,也更复杂
val list = List(List(1,2,3,5),List(4,5,6,7,8,9))
def m(x:AnyRef) = x match {
//变量绑定模式
case e1@List(_,e2@List(4,_*))=>println("e1:" + e1 + ",e2:" + e2)
case _=>
}
//调用
m(list)//e1:List(List(1, 2, 3, 5), List(4, 5, 6, 7, 8, 9)),e2:List(4, 5, 6, 7, 8, 9)
---反编译代码:
public void m(Object x) {
if ((x instanceof List))
{
List e1 = (List)x;
Some localSome1 = List..MODULE$.unapplySeq(e1);
if ((!localSome1.isEmpty()) && (localSome1.get() != null) && (((LinearSeqOptimized)localSome1.get()).lengthCompare(2) == 0))
{
Object e2 = ((LinearSeqOptimized)localSome1.get()).apply(1);
if ((e2 instanceof List))
{
List localList2 = (List)e2;Some localSome2 = List..MODULE$.unapplySeq(localList2);
if ((!localSome2.isEmpty()) && (localSome2.get() != null) && (((LinearSeqOptimized)localSome2.get()).lengthCompare(1) >= 0))
{
Object localObject2 = ((LinearSeqOptimized)localSome2.get()).apply(0);
if (BoxesRunTime.equals(BoxesRunTime.boxToInteger(4), localObject2))
{
Predef..MODULE$.println(new StringBuilder().append("e1:").append(e1).append(",e2:").append(localList2).toString());localBoxedUnit = BoxedUnit.UNIT; return;
}
}
}
}
}
}
3) 正则表达式与模式匹配(略)
(1)Scala正则表达式
(2)正则表达式在模式匹配中的应用
4) for循环种的模式匹配
(1)变量模式匹配
for((x,y)<-Map("a"->1,"b"->2,"c"->3)) {
println(x + ":" + y)
}
(2) 常量模式匹配
//下面只有"b"被匹配到,输出2
for(("b",y)<-Map("a"->1,"b"->2,"c"->3)) {
println(y)
}
(3) 变量绑定模式匹配
//可以绑定变量xxx和yyy
for((xxx@"b",yyy@y)<-Map("a"->1,"b"->2,"c"->3)) {
println(xxx + ":" + yyy)
}
(4) 类型模式匹配
//类型value为字符串才能匹配到
for((x,y:String)<-Map("a"->1,"b"->"hello","c"->3)) {
println(x + ":" + y)
}
(5) 构造方法模式匹配
case class Dog(val name:String, val age:Int)
case class Cat(val name:String, val age:Int)
for (Dog(name,age) <- List(Dog("a",1),Cat("b",2),Dog("c",3))) {
println(name + ":" + age)
}
上面输出第1个和第3个
for (List(first,_*) <- List(List(1,2,3), List(4,5,6,7))) {
println(first)
}
(6) 序列模式匹配
可以用_*
5) 样例类和样例对象
样列类(case class):使用case修饰的类。就是增加了一些方法,比如unapply。用在上面的模式匹配.
1) 样例类:是一种特殊的类,可以用于模式匹配。case class是多例,case object是单例。
上面已经做了一个例子了,下面再做一个例子,用到了sealted.
trait中前面加入 seatled,表示密封类
a) 其修饰的trait,class只能在当前文件里面被继承
b) 并且用sealed修饰这样做的目的是告诉scala编译器在检查模式匹配的时候,让scala知道这些case的所有情况,scala就能够在编译的时候进行检查,看你写的代码是否有没有漏掉什么没case到,减少编程的错误。
例:
sealed trait A
case class B(id:String,name:String) extends A
case class C(id:String) extends A
case class D(id:String) extends A
def m(x:A) = x match {
case B(id,name)=>"1"
case C(id)=>"2" // 这一行如果不写,系统会警告,这样的好处是提示你不要漏了
case D(id)=>"3"
}
//测试
val b = new B("1","zhang3")
println(m(b))
// 注意,如果注释掉上面的B,C,D中的任何一句,系统会警告,所以在子类众多的时候,要用 sealed修饰带来的好处。
2) 样例对象(case object)
上面看到每个子类都有自己的属性,但是实际开发,可能不需要属性,如果不定义属性,编译器会警告。
这时候需要定义样例对象(case object)来声明
sealed trait A
case class B(id:String,name:String) extends A
case class C(id:String) extends A
case class D(id:String) extends A
case object E extends A
def m(x:A) = x match {
case B(id,name)=>"1"
case C(id)=>"2"
case D(id)=>"3"
case E=>"4"
}
//测试
val b = new B("1","zhang3")
println(m(b))
println(m(E)) //直接调用
分析:
a)case class会先创建对象,而case object可以直接使用
b)case class会生成两个字节码文件,而case object是一个
c)case class生成的伴生对象会自动实现 apply和unapply方法,而case object不会
使用 case object可以提高速度,减少开销
例2:spark中的一段代码如下,看学生是否看懂?
import scala.util.Random
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask //定义样例对象
object Test extends App {
var arr = Array(CheckTimeOutTask, HeartBeat(2333), SubmitTask("0001", "task-001"));
arr(Random.nextInt(arr.length)) match {
case SubmitTask(id, name) => { println(s"$id,$name") }
case HeartBeat(time) => { println(time) }
case CheckTimeOutTask => { println("check") }
}
3) Option类型
Option类型也是样本类,表示值是可选的,要么无值,要么有值,Option的子类有Some和None
用Some(x) 表示有值,x是真正的返回值
用None表示无值
val map = Map("cn"->1,"jp"->2)
val v = map.get("cn") match { //get方法返回 Option类型
case Some(i) => i
case None => 0
}
查看map集合的源代码: def get(key: A): Option[B]
查看some的源代码:case class Some[+A](x: A) extends Option[A]
查看None的源代码:case object None extends Option[Nothing] {
我们常用 getOrElse("c",0)
9 隐式转换
1)隐式转换简介
scala提供的一种强大的语法特征,必须掌握。开发中,会自动转换。
scala定义了一些隐式转换,会自动导入他们。
比如 1 to 5 调用 1.to(5),但是to并不是Int类型的方法,它会自动转换为 scala.runtime.RichInt
源代码:系统自动导入Predef对象,在scala.Predef中,objectPredef父类LowPriorityImplicits中方法intWrapper(x:Int)=new runtime.RichInt(x)
不仅如此, Int还可以转换为其它类型(AnyRef,Integer,Double,Float,Long),可以由其它类型转换而来
查看所有的隐式转换
scala>:implicit -v
scala.Int的伴生对象包含了Int到其它类型的转换
implicit def int2long(x:Int) : Long = x.toLong
implicit def int2float(x:Int) : Float = x.toFloat
Predef对象中也可以了大量的隐式转换函数,如Scala数值类型到java数值类型的转换
implicit def byte2Byte(x:Byte) = java.lang.Byte.valueOf(x)
正是这些隐式转换函数的存在,简化了Scala代码。
2) 隐式转换
(1)隐式转换函数
Scala默认提供了大量的隐式转换,比如Int到Float,但是如果想实现Float到Int,需要自己定义
例:
val value : Int = 1.0f // 报错
修改:
implicit def m(x:Float) = x.toInt //toInt方法有,但是没有隐式转换
val value : Int = 1.0f
原理:当编译机发现类型不匹配,就会查找隐私转换函数
--------xjad反编译-----------
public int m(float x){
return (int)x;
}
int value = m(1.0F);
System.out.println(value);
}
注:名称可以是任意(与参数类型和返回值有关),但不要重复,否则隐式转换报错,不过尽量给名字写的有意义
(2) 隐式类
隐式类的转换不像隐式转换函数那么明显。
例;
implicit class A (val name:String) {
def bark = println(name + " is barking")
}
//调用
"Nacy".bark //由于String没有 bark方法,因此会找隐式类,由于A的主构造方法是String,就会调用。
---源代码
public static class Test$A{
private final String name;
public Test$A(String name) {this.name = name}
public void bark(){
System.out.println(name + " is barking");
}
}
public final class Test${
public void main(String args[])
{
new Test.A("Nacy").bark();//把“nacy”转换为创建A的对象
}
}
(3) 隐式对象
单例对象前加入implicit关键字
trait A[T] {
def m(x:T):T
}
//隐式转换对象:整数的平方
implicit object AInt extends A[Int]{
def m(x:Int) = x * x
}
//隐式转换对象:字符串的乘积
implicit object BString extends A[String] {
def m(x:String) = x * 2
}
//定了一个函数:函数具有泛型参数
def foo[T:A](x:T) = {
val obj = implicitly[A[T]] //通过 implicitly方法,知道T的类型,创建A的相应子类对象
obj.m(x)
}
//调用
println(foo(5)) //25,从下面源代码可以看到,翻译成java时传递两个参数
println(foo("5"))//"55"
------源代码----
public final class Test${
public Object foo(Object x, Test.A o) { //传递A的子类对象
Test.A obj = (Test.A)(o);
return obj.m(x);
}
public void main(String args[]) {
foo(5,传递Test.AInt的单例对象);
}
}
public final class Test{
interface A{
Object m(Object obj);
}
}
public static class AInt implements A{
public int m(int x){ return x * x; }
}
public static class BString implements A{
public String m(String x){ return x * 2}
}
(4) 隐式参数
上面的implicitly方法换一种形式,定义在参数中 , 参数前加了implicit,这种形式的参数成为隐式参数
上面代码可以化简如下:
def foo[T:A](x:T)(implicit obj:A[T]) = { //知道T的类型,创建A的相应子类对象
obj.m(x)
}
//编译后的源代码基本不变
(5) 隐式值
隐式值也叫隐式变量,前面用implicit修饰
例1:
implicit val x : Double = 2.55
def sqrt(implicit y: Double) = Math.sqrt(y)
//测试
scala> sqrt //不指定参数,编译器再当前作用于查找相应的隐式值或隐式对象,找到x传给它
--xjad反编译
public final class Test${
private final double x = 2.5499999999999998D;
public double sqrt(double y) {
return Math.sqrt(y);
}
public void main(String args[]){
sqrt(x);
}
}
例2:
改写上面的代码:
trait A[T] {
def m(x:T):T
}
class AInt extends A[Int]{ //普通类
def m(x:Int) = x * x
}
class BString extends A[String] {
def m(x:String) = x * 2
}
implicit val a = new AInt //隐式值,上面的例子是隐式对象
implicit val b = new BString
def foo[T:A](x:T)(implicit obj:A[T]) = {
obj.m(x)
}
//调用
println(foo(5)) //25
println(foo("5"))//"55"
3) 隐式参数使用常见问题
1. 当函数没有柯里化,implicit关键字组用于函数列表中的所有参数
scala>def m(implicit x:Double,y:Double) = x * y //所有参数都作用implicit
m: (implicit x: Double, implicit y: Double)Double
scala>implicit val d = 2.5
scala>m //结果 6.25
2. 隐式函数使用时要么全部指定,要么全不指定,不能只指定部分
如果上面调用
scala>m(3.0) 就报错
scala>m(3.0,3.0)
3. 同类型的隐式值只在当前作用域内出现1次
如果再定义
scala>implicit val d2 = 3.5
scala>m // 报错
4. 在定义隐式函数时,implicit关键字只能在参数开头
//报错,不能定义在第2个参数
def m(x:Double,implicit y:Double) = x * y
5. 如果想达到函数def m(x:Double,implicit y:Double)只将参数y定义为隐式函数的目的,需要对函数柯里化
//柯里化后,只有参数y是隐式参数
def m(x:Double)(implicit y:Double) = x * y
implicit val d = 4.0
m(3)
6. 柯里化的函数implicit关键字只能作用于最后一个参数
def m(x:Double)(y:Double)(implicit z:Double) = x * y * z //正确
def m(implicit x:Double)(y:Double)(z:Double) = x * y * z //错误,没有作用于最后一个
7. implicit关键字只在隐式参数中出现1次,对柯里化的函数也不例外
def m(implicit x:Double,implicit y:Double) = x * y // 错误
8. 匿名函数不能使用隐式参数
val m = (x:Double, y:Double)=>x * y // 正确
val m = (implicit x:Double, y:Double)=>x * y
9. 柯里化的函数如果有隐式参数,则不能使用其偏应用函数
def m(x:Double)(y:Double) = x * y
val t1 = m _ //赋值给t1, 定义两个参数的偏函数
t1(3,0)(4,0)
val t2 = m(3.0) _
t2(4.0)
以上都没有问题,但是如果下面有隐式参数,则会报错
def m(x:Double)(implicit y:Double) = x * y
val p = m _ //报错
4)隐式转换规则与问题
(1) 隐式转换的若干规则
a) 作用域规则
必须在当前作用域才能起作用。
c) 无歧义规则
不能存在两个一样功能的隐式转换函数。
d) 一次转换原则
只经过1次隐式转换,不能经过多次
(2) 是否调用隐式转换
非必要的情况下,尽量少用,如果是为了炫耀,大胆使用。
10 类型参数
1) 简介
(1) 简介
在类的声明中,定义一些泛型,然后在类内部,就可以使用这些泛型类型。
scala泛型和java泛型基本相同,只是Java语法格式为<>,而Scala语法给是为[]
//比如: java中List<T> ,而Scala中为 List[T]
//比如:
trait Map[A,B] extends Iterable[(A,B)] with GenMap[A,B] with scala.collection.Map[A,B] {
override def empty:Map[A,B] = Map.empty
}
(2) 泛型类
class Person[T](var name :T)
class Student[T,S](name:T, var age:S) extends Person(name)//name前不写var表示继承
例:
新生入学,填写id时候,有的人用整数,有的人用字符串
class Student[T](val id:T) {
def get(hukouId:T) = id + ":" + hukouId
}
val a = new Student[Int](111)
a.get(4)
val a = new Student(111) //不写Int,可以自动推断
val a = new Student[String]("111")
//测试
class Student[A,B](var name:String, var age:Int)
println(new Student[String,Int]("a",1).name)
(3) 泛型方法
与泛型类类似,给某个函数在声明时指定泛型类型
def getCard[T](content: T) = {
if (content.isInstanceOf[Int]) 1 //等价于java的instanceOf
else if (content.isInstanceOf[String]) 2
else 3
}
//测试
getCard(111) //1
getCard("222") //2
getCard(false) //3
(4) 类型通配符
在java中,采用List<?>来做,但是scala中,没有采用上面的特性,是通过存在类型来解决
类[T,U,..] forSome {type T;type U;...}
例:
//存在类型Array[T] forSome {type T}
def printAll(x : Array[T] forSome {type T}) = {
for (i <- x) {println(i)}
}
---源代码:
public void printAll(Object x) {
Predef$.MODULE$.genericArrayOps(x).foreach(new Serializable() {
public final void apply(Object i) {
System.out.println(i);
}
});
}
//可以写简化形式
def printAll(x : Array[_]) = {
for (i <- x) {prntln(i)}
}
2) 类型变量界定
对泛型范围进行界定,而不是任意类型。比如必须是某个类的子类,或者是父类,才能正常使用。
java中,这么定义:
//List<? extends Object> 接收object的子类,包括Object
//List<? super Integer> 接收Integer的父类,包括Integer
scala没有采用,它采用对应
<:
>:
例1:
class Person(val name: String) {
def makeFriends(other: Person) = println(this.name + "和" + other.name + "交朋友")
}
class Student(name: String) extends Person(name)
class Party[T <: Person](p1: T, p2: T) {
def play = p1.makeFriends(p2)
}
//调用
var p1 = new Student("zhang3")
var p2 = new Student("li4")
new Party[Student](p1, p2).play
例2:
def compare[T <:Comparable[T]](first:T,second:T) = {//表示Comparable的子类
if (first.compareTo(second)>0)
first
else
second
}
调用;:compare("A,"B”) //B
3) 视图界定
用<%表示: 相当于上面的<:加上隐式转化
class Person(val name: String) {
def makeFriends(other: Person) = println(this.name + "和" + other.name + "交朋友")
}
class Teacher(var name: String)
class Party[T <% Person](p1: T, p2: T) {
def play = p1.makeFriends(p2)
}
implicit def teacher2Person(t: Teacher) : Person = new Person(t.name)
//调用
var p1 = new Teacher("zhang3")
var p2 = new Teacher("li4")
new Party[Teacher](p1, p2).play
4) 上下文界定
回顾以前:
>: 继承:要求是长辈
<: 继承:要求是低辈
<% 继承+隐式转换
这次
T:A T是一个泛型, A是个类,要求调用这个类的函数时,必须要隐式引入A[T]。
举个例子:T是整数Int,A是Ordering(理解成排序comparator,系统有这个类),要求使用时候要引入隐式值Ordering[Int]
class Test[T:Ordering](val a:T,val b:T) {
def bigger(implicit myComparator:Ordering[T]) {
if (myComparator.compare(a,b)>0)
println(a +":"+ myComparator.getClass)
else
println(b)
}
}
new Test(1,2).bigger //2
----反编译----
public class Test{
private final Object a;
private final Object b;
public Test(Object a, Object b,){this.a = a;this.b = b; }
public void bigger(Ordering myComparator){
if (myComparator.compare(a(), b()) > 0)
System.out.println(a());
else
System.out..println(b());
}
}
new Test(1,2).bigger(系统自带的实现Int的Ordering对象)
5) 多重界定
(1) T:A:B 隐式引入A[T],B[T]
(2) T<%A<%B 存在T到A,T到B的隐式转换
(3) T1>:T<:T2 T1是T类型的超类,T2也是T类型的超类
class A[T]
class B[T]
implicit val a = new A[String]
implicit val b = new B[String]
//1.当前作用域中,必须存在两个隐式值,类型为 A[T],B[T]
def test[T:A:B](x:T) = println(x)
test("测试1")
implicit def t2A[T](x:T) = new A[T]
implicit def t2B[T](x:T) = new B[T]
//2.当前作用域中,必须存在T到A,T到B的隐式转换
def test2[T <% A[T] <% B[T]](x:T) = println(x)
test2("测试2")
6) 协变与逆变
//解决问题,如果Child是Father的子类,那么List<Child>是不是List<Father>的子类,答案:不是,但这会造成很多问题。
但Scala只要灵活运用协变与逆变,就可以解决这个问题
协变 +T 当A是B的子类, 那么func(A) 是func(B)的子类
例:
class Father
class Child extends Father
class Test[+T]
var x = new Test[Father]
var y = new Test[Child]
//此事,x也是y的父亲
x = y //正确
y = x //错误
逆变 -T
当A是B的子类, 那么func(A) 是func(B)的父类
===========================================================================
11 scala并发编程基础(讲义已经单独做成doc文档:讲义-Actor编程.docx)
1) 简介
(1) 并发和并行
并发:在一台处理器上“同时”处理多个任务,一个时刻只有一个任务跑
并行:在多台处理器上同时处理多个任务,一个时刻有有个任务跑。
优化:
并发:增加进程或者线程,纵向扩展
并行:增加cpu,横向扩展
Actor模型:可以同时解决纵向和横向扩展