配色: 字号:
scala
2018-08-16 | 阅:  转:  |  分享 
  
Scala总结篇0、基本概念:Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于Ja
va平台(Java虚拟机),并兼容现有的Java程序。Scala语言表达能力强,一行代码抵得上Java多行,开发速度快;Scala
是静态编译的,所以和JRuby,Groovy比起来速度会快很多注:Scala也是要求运行在jvm上,并且对于最新版本的Scala要
求jdk必须是1.8版本1、基本数据类型数据类型描述Byte8bit(1位)Short
16bit(2位)Int32bit(4位)LongFloat32bitDoub
le64bitChar16bitStringBooleanUnit
表示无值,和其他语言中的void相同Null空值或引用空值Noting所有其他类型的子
类型,表示没有值Any所有类型的超类,任何实例都属于Any类型AnyRef所有引用类型
的超类2数据的声明与定义在Scala中声明变量的关键字只有两个(1)val用于声明常量,类似于Java中的final,该修饰的
值是不能改变的(鼓励使用),val修饰的变量称为数值(2)var用于声明变量,该变量可以重复赋值3、Scala的基本结构(1)
结构object、class、Trait(特质相当于接口)如:traitScalaTest{}classScalaTes
t{}objectScalaTest{defmain(args:Array[String]):Unit={vali=3
varj=2varp:String="a"println(i+j)}}结果为5注:defmain(args:Arr
ay[String]){}是入口函数Unit相当于void指明无返回值。若是不写:Unit=直接接括号则也
表示无返回类型,若是只用=号则表示有任意的返回类型(注意类比Java中publicstaticvoidmain(St
ring[]args))在变量和函数的定义中,无需指定变量类型,系统会根据你给变量赋值的类型来推断变量类型。这一点在cm
d窗口中有明显效果展示。当然也可以添加变量类型,注意变量名在前,变量类型在后,用:分隔。如:varp:String="
aa"其中p就是变量名,而aa则是变量值,String是变量类型(2)条件结构Scala的条件if结构是有返回值的,其返回值的类
型系统会自动识别,无需指定返回值的类型。这与Java中的不同。objectHH{defmain(args:Array[St
ring]):Unit={vara=3
规范格式vary=if(a>1)trueelsefalseprintln(y)结果为true
支持混合类型(如true和2是两
种不同的类型)varx=if(a<(-1))trueelse2println(x)结果为2
支持缺省类型(缺省了else)varu=if
(a<3)trueprintln(u)结果为()vari=if(a>4)trueelse()println(i)结果
为()支持复合条件v
ark=if(a<1)trueelseif(a>2)trueelsefalseprintln(k)结果为true}}
(3)块表达式块表达式的值等于块中最后一个表达式的值。可见如下两例:objectHH{defmain(args:Array[
String]):Unit={vara=1varb=2varc=3varf=if(a<0){a;a+1}el
se{a+b;b+c}println(f)-----------------5varff={a+bc+ac+ba+
b+c}println(ff)----------------------6}}(4)while循环objectHH{def
main(args:Array[String]):Unit={vari=9;while(i>0){println(i)
i-=1}}}注:while循环是含括号的在Scala中是没有++和--的,因此只能用i-=1来代替--(5)f
or循环注意:for(i<-1to2){}中i不用声明<-是一个符号,表示将1到2的数赋值给i,这类似于
Java中增强型for循环注意:循环体加括号也可以注意:for循环中表达数值区间的由三种方式(1)表达式to和
until1to3表示1,2,31until3表示1,2(不含右边界)(2)数组
vararr=Array("a","b","c")for(i<-arr)(3)集合for循环的特殊格式:(1)
包含双层for循环,且含判断条件:for(i<-1to3;j<-1to3ifi!=j){i+j}注:i<-1
to3;这是第一层for循环;j<-1to3这是第二层for循环,ifi!=j这是第二层for循环中的条件;在Ja
va中相当于for(inti=1;i<=3;i++){for(intj=1;j<=3;j++){if(i!=j){i
+j}}}(2)包含yield的for循环valv=for(i<-1to10)yieldi1
0;println(v)结果:Vector(10,20,30,40,50,60,70,80,90,100
)注:若for循环体以yield开头,则每执行一次后面的循环体,生成的值都会被yield构建成一个集合中的元素。最终形
成一个集合;案例(1)for(i<-(-2)to3)println(i)结果-2-10123(2)for(i<-1
until3)println(i)结果12(3)vararr=Array(1,2,4)for(x<-arr){p
rintln(x)}(4)for(i<-1to2;j<-3to4ifj>i){println(i+j);}
注意:此时也可以写成for(i<-1to2;j<-3to4if(j>i)){println(i+j);}Sc
ala有3种类:一种是class,一种是object;还有一个是特质(trait)class是非静态,而object内部都是静态
.trait内部即可有抽象方法,又可以有非抽象方法(在java8中对于接口来说是可以有非抽象方法的)当object的名与clas
s的名相同时,则object是class的半生对象;class是object的半生类。半生对象与半生类可以互为使用各自的私有
方法。若在半生类中是private[this]valheight=180这个私有更严格,半生对象也不可用。1)注意
函数和方法是不同的。函数是没有静态和非静态只说的。同时函数的执行并不是从main方法开始的,(通常我们使用函数是将函数作为一个参数
传递给方法的,从函数定义方式来说,都是通过var来定义的因此可以将函数做为变量来使用)2)scala中的class与objec
t要区分开对于Scala的方法而言,并没用静态static的类型,不过object相当于静态。object中的成员都是静态的。5
、方法的定义defmm(x:Int,y:Int):Unit={方法体}函数的定义vals=(x:Int,y:
Int)=>{x+y}或(x:Int,y:Int)=>{x+y}函数不用指定返回类型,系统会自动识别其返回类型如
varfun=(x:Int,y:Int)=>{x+y}此时fun的值就是x+y的值;由此需要注意:对于函数和方法
,其函数体或方法体的最后一个语句会自动作为返回值返回,而无需写return;所以你在向函数传入参数时,函数会自动返回值在map(x
=>{println(x)})就是向map()方法中传入一个函数,x=>{println()},也可以写成map((x:Int)=
>{println(x)})显然我们是将一个完整的函数表达做为方法的一个参数传入到map方法中,同时传入map中的值会自动赋值给x
.6、在Scala中函数是头等公民,它可以像其他数据类型一样被传递和操作objectHH{//先定义一个方法,其参数为一个函数
defm(f:(Int,Int)=>Int):Int={f(2,3);----这叫方法体}上述表达告诉我们如和定义一
个参数为函数的方法的格式。//根据方法的参数需求,我们定义一个函数来作为上述方法中的参数varf1=(x:Int,y:In
t)=>{x+y-----这叫函数体}defmain(args:Array[String]):Unit={p
rintln(m(f1));}}注:如果这个函数不能自动返回值的话那么外层的方法体的值是什么?如果外层的方法体不能自动返回值的话
,那么你println(m(f1));用能输出什么?7将方法转化为函数:定义一个方法defm1(x:Int,y:Int)
:Unit={x+y}将该方法转为一个函数(注意下划线)valf1=m1_此时f1就是函数8、数组Scala的数组有两种,
并且每种都有常用的两种定义方法:数组的定义:(1)定长数组定义:vararr1=newArray[Int](18)
---定义一个长度为18,元素为Int型的定长数组arr1,(注:用于初始化数组没有给定元素,因此必须指定元素类型)
vararr2=Array(1,2,"a",4)-----定义一个定长数组,数组元素已经给定,系统会根据给定的元素
类型自动判定数组的元素类型注:在Java中由于无论是哪种创建方式,都需要先指定元素类型,因此数组的元素类型是一样的,而Scala
的第二种方式则不是(2)变长数组创建变长数组需要导包importscala.collection.mutable.ArrayB
uffer定义:vararr3=newArrayBuffer[Int]()vararr4=Ar
rayBuffer(1,2,3,4)注:这里千万不要与后面的集合弄混,后面的集合也会涉及mutable的问题,不过集合所涉及的是
集合元素变化的问题。而此处的数组涉及的是数组长度的可变性问题。同时可不可变跟var还是val没啥关系只不过var修饰的引用型
变量其地址可变val修饰的引用型变量其地址不可以变赋值:(1)定长数组vararr1=newArray[Int]
(12)arr1(0)=1arr1(1)=2(2)变长数组赋值(三种)vararr2=newArrayBuffer[
Int](12)arr2+=1arr2+=(1,2,3,4)----(1,2,3,4)是元祖arr2++=Arra
y(1,2,4)arr2++=ArrayBuffer(1,2,43,4)insert如:vararr3=Arra
yBuffer("a",1,''a'');arr3.insert(0,-1,-2,-3)-------------第一位数表示位置,
后面的数表示的是添加的元素println(arr3(2))remove如:vararr3=ArrayBuffe
r("a",1,''a'');arr3.remove(0,2)-----------------------其中0表示从第0位开始(
包含第0位),删除两个数for(i<-arr3){println(i);}(4)数组遍历使用for循环进行遍历for
循环遍历可使用数组、表达式、集合等进行循环。需要注意的是reverse这一函数,表示翻转的意思如:objectHH{
defmain(args:Array[String]):Unit={vararr=Array(1,2,3,4,5)
for(i<-arr.reverse){println(i)}结果:5,4,3,2,1(5)数组的转换将指定数组转换为新数
组有两个函数方法yield和map1)yield借助对指定数组的遍历(for),yield方法从中获取数组的元素
,并做指定的运算,然后再次形成一个数组,并返回如下:objectHH{defmain(args:Array[String])
:Unit={vararr=Array(1,2,3,4,5)varv=for(i<-arr)yiel
di2for(i<-v){println(i)}2468102)mapmap相当于将数组中的每一个元素取出来
,应用传进去的函数,然后在形成一个新数组。而原数组不变。vararr=Array(1,2,3,4,5)如varf
=(x:int)=>x2varar=arr.map(f)for(i<-ar){println(i)}
注:arr.map(f)还可以写成arr.map((x:int)=>x2)、arr.map(x=>x2)、arr.
map(_2)其中_是占位符(使用占位符,的前提是若有两个参数,则这两个参数不能相同,若只用一个参数,则可以直接使用)之所
以能够省略,是因为它知道传递参数的类型3)Array转为ArrayBufferprintln(Array.toBuffer)注:
如果直接println(arr)当arr是Array时,则输出的是hachcode,若arr是ArrayBuffer时,
则输出的是数组中的数据。(6)数组的两种声明var和valvar修饰的数组其地址值可以发生改变如:vararr1=new
Array[Int](10)vararr2=newArray[Int](12)arr1=arr2这是没有问题
的val修饰的数组的地址值是不可变的。如:valarr1=newArray[Int](10)valarr2=ne
wArray[Int](12)arr1=arr2是会报错的val相当于Java中的final。修饰引用类型时,指的是地址
不变;修饰一个普通变量时,指的是常量(7)数组常用的方法(sum、max、min、sorted)vararr=Array
(1,2,3,4,5,3,12);vari=arr.sumprintln(i)varii=arr.maxprin
tln(ii)variii=arr.minprintln(iii)variiii=arr.sorted自然排
序println(iiii.toBuffer)9、映射构建映射importscala.collection.mutable.M
ap(1)用->的方法varmap=Map("xx"->12,"yy"->32)(2)用元祖的方法varmap=Map
(("xx",21),("yy",32))获取映射varm=map("xx")println(m)---->21修改映射
map("xx")=88追加映射map+=("zz"->12,"kk"->32)结果为:Map(yy->32,zz->
12,xx->12,kk->32)10、元祖:(tuple)元祖的最简单形式就是键值对形式映射是K/V对偶的集合,对
偶是元组的最简单形式,元组可以装着多个不同类型的值。元祖是由小括号将一个或多个相同类型或不同类型的数括起来,元素之间用逗号分隔。如
varxx=("a","b","c")varxx=("a","b","c")println(xx)-------
--("a","b","c")println(xx._1)--------aprintln(xx._2)--------bpr
intln(xx._3)--------c注:元祖的创建有两种表示方法vara=(1,2,3,4)varaa=new
Tuple(1,2,3,4)实时上a是aa的简写,元祖的类型是Tuple2Tuple3等,也就是说在表示元祖类型时,是用Tup
le来表示的Tuple本身是一个case类,在存入list时,会有区别varlist=newBufferList[(In
t,Int)]()list+=Tuple2(1,2)varlist=newBufferList[Int]()list+
=(1,2)11.将对偶的数组转成映射如:vararr=Array(("aa",12),("vv",13))println
(arr.toMap)--->aa->12vv->1312.拉链操作:zip拉链操作就是将两个数组中对应位置的元素放到一起,
形成对偶数组vararr=Array("zs","ls","wu")vararr1=Array(12,23,34)va
ra=arr.zip(arr1);println(a.toBuffer)//将对偶数组转为映射println(a.
toMap)结果:ArrayBuffer((zs,12),(ls,23),(wu,34))Map(zs->12,ls-
>23,wu->34)14.List集合(序列)不可变序列varlist=List(1,2,3);追加元素后
,会生成新的序列可变序列varlist=ListBuffer(1,2,3)list+=4list.append(5
)向list添加元素,注意当list是不可变的时候,一旦向其中添加元素,那么就会产生新的集合如varlist1=List
(1,2,3)varlist2=list1.::4结果:list2为1,2,3,4注:.::表是的是将
右边的元素添加到左边的集合中,同时形成新的集合list1.::=4类似于+=的操作,表示将右边的元素添加到左边的集合,
再将新生成集合再次赋值给list1最终虽然集合名是list1,但此时的集合已经是新的集合了15.Set集合定义:不可变集合(
添加元素时会产生新的集合)varst=HashSet(1,2,3)varst1=newHashSet[Int](
)可变集合(当添加元素时,不会产生新的集合)varst=mutable.HashSet(1,2,3)varst1=
newmutable.HashSet[Int]()添加元素st+=4st.add(4)--->只用于可变集合st++=st
1st.remove(1)---这里的1表示的是位置,注集合的位置是从1开始的,数组的位置是从0开始的16.Map集合定义:定义
的是可变集合varmap=newmutable.HashMap[String,Int]();添加键值对map+=(("aa
",11))map.put("bb",22)map("cc")=33plintln(map)------>Map(bb->22
,cc->33,aa->11)删除键值对map-="aa"map.remove("bb")println(map)--
------>Map(cc->33)获取元素:map(cc)----->3317单词统计(groupby,flatMap)定
义一个数组vararray=Array("a,b,c","a,b,c")然后进行统计(从分组后,数组的所含元素的个数角度去统
计个数)vara=array.flatMap(_.split(","))a.map(x=>(x,1)).groupBy
(tuple=>tuple._1).foreach(t=>println(t._1+"->"+t._2.length))具体:(1
)定义数组scala>vararray=Array("a,b,c","a,b,c")array:Array[String
]=Array(a,b,c,a,b,c)(2)分割,并压缩(也就是将多个数组压缩成一个数组,我们利用flatMap来实现)s
cala>vara=array.flatMap(_.split(","))a:Array[String]=Array
(a,b,c,a,b,c)(3)创建键值对scala>a.map(x=>(x,1))res0:Array[(Stri
ng,Int)]=Array((a,1),(b,1),(c,1),(a,1),(b,1),(c,1))(4)借助元
祖,将其进行分组groupBy,分组的结果是形成了一个Map[String,Array[(String,Int)]]映射如:M
ap[String,Array[(String,Int)]]=Map((b->Array((b,1),(b,1)),a
->Array((a,1),(a,1)),c->Array((c,1),(c,1)))借助元祖,将进行分组scala>
a.map(x=>(x,1)).groupBy(tuple=>tuple._1).foreach(t=>println(t._1
+"->"t._2.length))b->2a->2c->218、lazylazy是Scala提供的一个特性,如果用lazy
去修饰一个变量,则这个变量只有在第一次使用时才会发生计算,而不会再定义时就计算。这个效果可以通过命令窗口观察到。例如:加载一个文件
对象(c://t.txt文件并不存在)valfile=Source.fromFile("c://t.txt")结果:ja
va.io.FileNotFoundException:c:\t.txt(系统找不到指定的文件。)atjava.io.Fi
leInputStream.open0(NativeMethod)atjava.io.FileInputStream.ope
n(FileInputStream.java:195)atjava.io.FileInputStream.(Fil
eInputStream.java:138)atscala.io.Source$.fromFile(Source.scala:
91)atscala.io.Source$.fromFile(Source.scala:76)atscala.io.Sou
rce$.fromFile(Source.scala:54)...32elided若使用lazylazyvarfile
1=Source.fromFile("c://t.txt")结果file1:scala.io.BufferedSource
=没有报错,是因为lazy修饰的变量,只有在第一次使用时才会计算for(line<-file1.getLines
)println(line)结果:java.io.FileNotFoundException:c:\t.txt(系统找不到指
定的文件。)atjava.io.FileInputStream.open0(NativeMethod)atjava.io
.FileInputStream.open(FileInputStream.java:195)atjava.io.FileIn
putStream.(FileInputStream.java:138)atscala.io.Source$.fr
omFile(Source.scala:91)atscala.io.Source$.fromFile(Source.scala
:76)atscala.io.Source$.fromFile(Source.scala:54)...32elided这
就说明了lazy修饰的变量,在第一次运行时才会计算见图:19、reduce方法如:defmain(args:Array[S
tring]):Unit={vararr=Array(1,2,3)println(arr.reduce((x:Int,
y:Int)=>x-2))}结果是-3reduce的运算规则是:先向reduce中传入一个函数。这个函数要求参数必
须是两个,而函数的函数体是任意的.然后取array中的第一个元素作为x第二个元素作为y,让这个x和y去执行指定函
数体的操作。将执行操作的结果再次作为x的值,再取array中的第3个元素作为y,再次让这两个x和y去参与函数体的运算。最终结
果作为reduce的输出结果。一般reduce都是reduceByKey。在scala中是reduceByKeysortByKe
y(你自己指定的一个排序key,boolean)groupByKey在Java中是:reduceByKeysortBy(
boolean)groupByKey20.构造器:分类:(1)主构造器每个类都有主构造器,主构造器的参数直接放置类名后面,与
类交织在一起。下面的案例中:Persion(valx:Int,valy:Int){}就是主构造器,
它与classPersion类交织在一起了。(注:我们说每个class中都有主构造器,若是主构造器为无参构造,那么这就是我们经
常见到的classPersion{})主构造器的参数会在全局生成变量,其作用相当于属性(2)辅助构造器辅助构
造器本质是一个方法,用def修饰,统一都命名为this,辅助构造器的参数必须包含主构造器的参数。每个辅助构造器必须以调用主构造
器或调用其他的辅助构造器开始辅助构造器的参数列表不能再用var或val修饰了辅助构造器位于主构造器体内。注:在理解Scala的构造
器时,可以按Java的构造器取理解,也是创建对象,就会调用构造器。例如:classPersion(valx:Int,val
y:Int){println("主构造器")defthis(x:Int,y:Int,z:Int){this(x,y)
println("辅助构造器1")}defthis(x:Int,y:Int,z:Int,w:Int){this(x,y,
z)println("辅助构造器2")}}objectHH{defmain(args:Array[String])
:Unit={varper=newPersion(1,3,1,2);}}执行结果:主构造器辅助构造器1辅助构造器
2执行顺序为根据参数的个数以及类型,首先执行defthis(x:Int,y:Int,z:Int,w:Int)辅助构造器由
于其内调用了this(x,y,z),因此再执行defthis(x:Int,y:Int,z:Int)辅助构造器。又由于defth
is(x:Int,y:Int,z:Int)中含有this(x,y)因此会跳转到主构造器classPersion(va
rx:Int,vary:Int)去执行注:若将主构造器声明为私有即classPersionprivate(var
x:Int,vary:Int){}则只能在其所对应的半生对象中调用该构造器。如:classPeopleprivate(v
alname:String,privatevarage:Int){println(name,age)}ob
jectHH{defmain(args:Array[String]){//私有的构造器,只有在其伴生对象中使用val
q=newPeople("hatano",20)}}就是错的:应该是:classPeopleprivate(va
lname:String,privatevarage:Int){println(name,age)}obj
ectPeople{defmain(args:Array[String]){//私有的构造器,只有在其伴生对象中使用
valq=newPeople("hatano",20)}}21、objectobject中的方法为静态方法,其中的
属性为静态属性object(必须是半生对象时)中的对象为单例对象,这里的对象是通过vala=Word,其中Wor
d是class的类名,a就是对象若你通过vala=newWord();的方式获得,那就不是单利的了。class中的
对象是多利的例如:classWord{}objectWord{defmain(args:Array[String
]):Unit={vara=newWord();varb=newWord();valc=Word;
vald=Word;println(a)println(b)println(c)println(d)}}结果:W
ord@3c5a99daWord@47f37ef1Word$@5a01ccaaWord$@5a01ccaa22对象半生对象
单利对象多利对象23、apply方法对于数组vararr=newArray[Int](10);vararr1
=Array(1,23,4,4,5);我们知道arr是通过new出来到堆内存的。那么arr1并没有new为什么也能创建数组与
堆内存呢?其实当程序执行到Array(1,23,4,4,5)时,会自动调用apply方法。该方法会自动根据所提供的参数(数组元
素)创建一个数组。apply的底层代码如下:defapply(x:Int,xs:Int):Array[Int]=
{valarray=newArray[Int](xs.length+1)array(0)=xvari=
1for(x<-xs.iterator){array(i)=x;i+=1}array}其中xs
:Int是动态参数列表valarray=newArray[Int](xs.length+1)本质还是new了
24、APP类使用object对应的类去继承APP应用扩展可以,不再写main方法了objectHHextendsAp
p{vararr1=Array(1,23,4,4,5);println(arr1.toBuffer)}25继承exte
nds在Scala中无论是实现还是继承我们都用extends来表达,在scala中没有implement的概念,无论继承
类还是trait,统一都是extends(1)若子类重写了父类的非抽象方法,则我们在子类中重写的那个方法需要用overrid
e来修饰。若重写抽象方法则不用写override,实现接口也符合这一条原理overridedefgetname=
"wojiao"+super.getname一旦加上override结果就是只要这个方法没有在父类中出现,那么系统就会报
错在子类中覆盖抽象类的抽象方法时,不需要使用override关键字(2)若我们就要从父类中调用那个被重写的方法,则可以用super
.这个与Java是一样的例如:classFather{privatevarname:String="父"defg
etname={name}---就是定义了一个方法,其方法体是name参数}classSonextendsFathe
r{privatevarscore:String="zi"defgetscore=scoreoverrided
efgetname="wojiao"+super.getname}objectHH{defmain(args:
Array[String]):Unit={vars=newSon();println(s.getname)}
}结果:wojiao父注:一定注意方法的定义,特别是无参的方法defa()=namedefa=nam
edefa={name}defa()={name}都是一样的26特质(trait)在Scala中有三大结构
class、object、trait(类似Java中的接口)对于trait而言,既然有接口特征,那么同样是多实现。若一
个类实现多个trait,那么第一个trait用extends连接其余用with(在Java中用的是,)如:class
PerSionextendsAwithBwithC在Java7以后接口中就可以含有具体方法了,同样trai
t也可以含有具体方法(一)将特质作为接口使用类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字t
raitHelloTrait{defsayHello(name:String)}traitMakeFriendsTrai
t{defmakeFriends(w:Worker)}classWorker(varname:String)extend
sHelloTraitwithMakeFriendsTrait{defsayHello(name:String)=p
rintln("hello,"+name)defmakeFriends(w:Worker)=println("hello,
mynameis"+name+"younameis"+w.name)}objectTest{defmain(arg
s:Array[String]){valp1=newWorker("xiaoma");valp2=newWorke
r("linghuchong")p1.sayHello("lihuchong")p1.makeFriends(p2)}}结果
:hello,lihuchonghello,mynameisxiaomayounameislinghuchong注:
对于主构造器中的参数列表是作用于整个class中的,类似于Java中的属性。(二)在trait中定义具体方法traitLog
ger{deflog(message:String)=println(message)}classPerson(val
name:String)extendsLogger{defmakeFridends(p:Person):Unit={
println("I''m"+name+"i''mgladetomakefriendswithyou"+p.name)
;log("makeFridendsmethodinvoked!!")------这里是因为继承关系所以直接引用即可(之前是
super.jdk1.8就直接用)}}objectTest{defmain(args:Array[String])
{valp1=newPerson("linpingzhi")valp2=newPerson("yuelingshan"
);p1.makeFridends(p2)}}(三)trait中定义一个具体字段traitPerson{vale
yeNum:Int=2}classStudent(valname:String)extendsPerson{defsa
yHello()=println("Hi,I''m"+name+"Ihave"+eyeNum+"eyes!")}obje
ctTest{defmain(args:Array[String]){vals=newStudent("zhang
sanfeng")s.sayHello();}}(四)trait中定义一个抽象字段在其实现类中必须给出具体的该抽象字段tra
itsayHello{valmsg:StringdefsayHello(name:String)=println(ms
g+","+name)}classPerson(valname:String)extendssayHello{va
lmsg:String="hello"defmakeFriends(p:Person):Unit={sayHell
o(p.name)println("I''m"+name+"Iwanttomakefriedswithyou")
}}objectTest{defmain(args:Array[String]){valp1=newPerson(
"zhangwuji")valp2=newPerson("zhangsanfeng")p1.makeFriends(p2)
}}(五)为实例对象混入traittraitLogged{deflog(msg:String){}}traitAMyL
oggerextendsLogged{overridedeflog(msg:String):Unit={print
ln("test:"+msg)}}traitBMyLoggerextendsLogged{overridedeflo
g(msg:String):Unit={println("log:"+msg)}}classPerson(valnam
e:String)extendsAMyLogger{defsayHello():Unit={println("Hi
,i''mname")log(name)}}objectTest{defmain(args:Array[Strin
g]){valp1=newPerson("liudehua")p1.sayHello()valp2=newPers
on("zhangxueyou")withBMyLoggerp2.sayHello()}}结果:Hi,i''mna
melog:liudehuaHi,i''mnamelog:zhangxueyou注:关键理解valp2=newPerso
n("zhangxueyou")withBMyLogger其中withBMyLogger表是的是p2对象之后
调用的sayHello()方法来自于BMyLogger(六)责任链//Scala中支持让类继承多个trait后,依次调用多个
trait中的同一个方法,只要让多个trait的同一个方法中,在最后都执行super.方法即可//类中调用多个trait中都有
的这个方法时,首先会从最右边的trait的方法开始执行,然后依次往左执行,形成一个调用链条traitHandler{def
handler(data:String){}}traitDataValidHandlerextendsHandler{ov
erridedefhandler(data:String):Unit={println("checkdata:"+da
ta)super.handler(data)}}traitSignatureValidHandlerextendsHan
dler{overridedefhandler(data:String):Unit={println("checks
ignatrue:"+data)super.handler(data)}}classPerson(valname:Str
ing)extendsSignatureValidHandlerwithDataValidHandler{defsay
Hello={println("Hello"+name)handler(name)}}objectTest{defm
ain(args:Array[String]){valp=newPerson("lixiaolong");p.sayH
ello}}结果Hellolixiaolongcheckdata:lixiaolongchecksignatrue:lix
iaolong27模式匹配(1)字符串匹配变量match{casecasecase}importscala.util.Ra
ndomobjectCaseDemo01extendsApp{vararr=Array("Hadoop","HBsa
se","Spark")valname=arr(Random.nextInt(arr.length))namema
tch{case"Hadoop"=>println("ha")case"HBase"=>println("H")
case_=>println("fdsaf")}}(2)匹配类型importscala.util.Randomobjec
tCaseDemo01extendsApp{vararr=Array("Hadoop",123,''a'')valn
ame=arr(Random.nextInt(arr.length))namematch{casex:Strin
g=>println("zifuchai")casey:Int=>println("int")casez
:Char=>println("char")case_:Char=>println("gp")}}(3)匹配数
组objectCaseDemo03extendsApp{valarr=Array(1,3,5)arrmatc
h{caseArray(1,x,y)=>println(x+""+y)caseArray(0)=>
println("only0")caseArray(0,_)=>println("0...")case_=>
println("somethingelse")}(4)匹配序列vallst=List(3,-1)lstma
tch{case0::Nil=>println("only0")casex::y::Nil=>pr
intln(s"x:$xy:$y")case0::tail=>println("0...")case_=
>println("somethingelse")注::表示将所有两边的数连接起来,再次形成一个Lis
t序列Nil表示的是空(5)匹配元祖valtup=(2,3,5)tupmatch{case(2,x,
y)=>println(s"1,$x,$y")case(_,z,5)=>println(z)case
_=>println("else")注:在模式匹配中每条case最后都有一个break,只不过是隐士的。28样例类样例类
主要用于模式匹配如下程序:中SubmitTask(id:String,name:String),HeartBeat(ti
me:Long),CheckTimeOutTask都是样例类importscala.util.Randomcaseclas
sSubmitTask(id:String,name:String)caseclassHeartBeat(time:
Long)caseobjectCheckTimeOutTaskobjectCaseDemo04extendsApp{v
alarr=Array(CheckTimeOutTask,HeartBeat(12333),SubmitTask("00
01","task-0001"))arr(Random.nextInt(arr.length))match{caseS
ubmitTask(id,name)=>{println(s"$id,$name")}caseHeartBeat(
time)=>{println(time)}caseCheckTimeOutTask=>{println("ch
eck")}}}29Option类型Option类型包含了两个子类Some:对值进行了封装None:没有进行封装任何
值如:objectOptionDemo{defmain(args:Array[String]){valma
p=Map("a"->1,"b"->2)valv=map.get("a")match{caseSom
e(i)=>println("a")caseNone=>println(0)}}}结果:a30偏函数偏函数
就是用来做模式匹配的,本质还是一个方法objectPartialFuncDemo{//偏函数第一个String是输入类
型也就是onetwo而输出则是Int类型也就是12-1整个偏函数的函数体就是一个匹配deffunc1:P
artialFunction[String,Int]={case"one"=>1case"two"=>2c
ase_=>-1}defmain(args:Array[String]){println(func1("one
"))}}结果是131、柯里化就是将一个方法(x:Int,y:Int,z:Int....)转为(x:Int)(y:Int)
(z:Int)的形式本质是一个方法在Scala的窗口中进行编程defm(x:Int)=(y:Int)=>xydeffun=
m(2)fun=m(3)结果为6在ideal编译objectTest{defk1(x:Int)(y:Int)=x
+ydefmain(args:Array[String]):Unit={valresult=k1(1)(2)
println(result)}}结果为3objectTest{defk2(x:Int)(implicity:Int=10)={xy}defmain(args:Array[String]):Unit={println(k2(3))}}结果为30注:其中的implicit表示的就是隐士转换。对于柯里化来说你需要将全部参数的值都传进去,若你没有将全部的值传进去。而是将一部分值放在了属性的位置,也就是上下文的位置或是该方法中,那么你就运用隐士转化参数implicit。系统首先会在上下文的作用域去寻找隐士化参数,最后才会去方法的参数列表中去询找隐士化参数。若在上下文中出现多个隐士化参数,那么由于系统不知使用的是哪个,一致报错。注意在参数列表的指定变量中必须加入implicit才行/CreatedbyAdministratoron2017/10/18./objectTest{implicitvalss:Int=5implicitvalsss:Int=6defk2(x:Int)(y:Int)(implicitz:Int)={xyz}defmain(args:Array[String]):Unit={println(k2(3)(4))}}32隐士转换(三种时机)隐士参数:用implicit修饰隐士函数(1)时机一当一个对象去调用某个方法的时候,这个对象并不具备这个方法。这个时候就会发生隐士转换,它就会去找一个隐士方法(能够把这个对象转换为具备想调用这个方法的对象)如Int想调用to方法,但Int不具备to方法。这个时候就是隐士转换发生的时机。Int想去找一个隐士转换方法(这个方法就是把Int转换为RichInt,因为RichInt有to方法)这样就可以调用to方法了,Int找隐士转换方法是自动进行的,当然这就要求隐士方法是要存在的。对于将Int转为RichInt的隐士方法就存在于系统中,intWrapper(x:Int)=newruntime.RichInt(x)就是这个隐士方法,它将传入的Int型的x转换为RichInt型(2)时机二当一个对象调用某个方法,这个对象确实也有这个方法。但是参数类型不匹配这个时候也会触发隐士转换(3)时机三视图边界A<%B左侧的类型必须是右侧的子类。若左侧的类型不是又侧的子类,则左侧的类型会隐士的转换为又侧的子类场景一packagecn.itcast.impliimportjava.io.Fileimportscala.io.Source//隐式的增强File类的方法,只有RichFile类中有read方法,而File中没有classRichFile(valfrom:File){defread=Source.fromFile(from.getPath).mkString}------------------------objectRichFile{//隐式转换方法implicitdeffile2RichFile(from:File)=newRichFile(from)}-------------------------objectMainApp{defmain(args:Array[String]):Unit={//导入隐式转换importRichFile._//导入RichFile下的所有东西println(newFile("c://words.txt").read)
献花(0)
+1
(本文系实习生101首藏)