Scala语言整理(一)
與Java對(duì)比
| 對(duì)比項(xiàng) | Java | Scala | 說明 |
| 標(biāo)識(shí)符 | / | / | 反引號(hào)括起來的字符也是標(biāo)識(shí)符。 |
| 語句結(jié)束 | 分號(hào)(;) | 分號(hào)(;)或者換行 | ? |
| 包 | package ... | package name ? package name{ } | 第2種類c#,可以一個(gè)文件定義多個(gè)package。 package 可嵌套。 |
| import | 文件開頭 指定類 指定包下所有類(.*) static import(引入靜態(tài)成員) | 任意位置 指定類 指定包下所有類(._) 包對(duì)象 | // 重命名成員 // 隱藏成員 默認(rèn)情況下,Scala 總會(huì)引入 java.lang._ 、 scala._ 和 Predef._,這里也能解釋,為什么以scala開頭的包,在使用時(shí)都是省去scala.的。 |
| 類 | class | class object | 一個(gè)Scala源文件中可以有多個(gè)類。object定義單例對(duì)象。 伴生對(duì)象實(shí)例 |
| 繼承,實(shí)現(xiàn) | extends ,implements | extends,with | class Student extends Person with FileLogger with Cloneable //實(shí)現(xiàn)了2個(gè)接口。 |
| 枚舉 | enum | extends??Enumeration | ?object WeekDay extends Enumeration{ ? ? type WeekDay = Value ?//聲明枚舉對(duì)外暴露的變量類型 ? ? val Mon = Value("1") ? ? val Tue = Value("2") ?... ? ? def checkExists(day:String) = this.values.exists(_.toString==day)? ? ? def isWorkingDay(day:WeekDay) = ! ( day==Sat || day == Sun)? ? ? def showAll = this.values.foreach(println) // 打印所有的枚舉值 ? } ? |
| 內(nèi)部類 | 匿名內(nèi)部類 成員內(nèi)部類 局部?jī)?nèi)部類 靜態(tài)內(nèi)部類 ? | 外部類class - 內(nèi)部類class 外部類class - 內(nèi)部對(duì)象object 外部對(duì)象object - 內(nèi)部類class 外部對(duì)象object - 內(nèi)部對(duì)象object | ?Java 中,內(nèi)部類是外部類的成員,而 Scala 正好相反,內(nèi)部類是綁定到外部對(duì)象的。 參考:https://docs.scala-lang.org/zh-cn/tour/inner-classes.html ? |
| 接口 | interface | trait | Trait可以定義屬性和方法的實(shí)現(xiàn),類似java抽象類。Java8 默認(rèn)方法。 |
| 方法 | () {...} | def | Scala方法體在等號(hào)后面; def functionName ([參數(shù)列表]) : [return type] = { |
| 函數(shù) | lambda表達(dá)式 | 賦值給變量 | ? |
| 閉包 | / | ? | 閉包是一個(gè)函數(shù),返回值依賴于聲明在函數(shù)外部的一個(gè)或多個(gè)變量。 "閉包",因?yàn)樗玫胶瘮?shù)外面定義的變量,定義這個(gè)函數(shù)的過程是將這個(gè)自由變量捕獲而構(gòu)成一個(gè)封閉的函數(shù)。 |
| 泛型 | <T> | [T] | ? |
| 數(shù)據(jù)類型 | / void null Object | / Unit Null AnyRef Nothing Any | 數(shù)據(jù)類型相同。Scala中沒有基本類型,全是對(duì)象類型。 Unit:Unit只有一個(gè)實(shí)例值,寫成()。 |
| 符號(hào)字面量 | 無 | ?'<標(biāo)識(shí)符> ,這里 <標(biāo)識(shí)符> 可以是任何字母或數(shù)字的標(biāo)識(shí)(注意:不能以數(shù)字開頭) | package scala 這種字面量被映射成預(yù)定義類scala.Symbol的實(shí)例。 |
| 多行字符串 | + 連接 | """ .... """? | ?val foo = """菜鳥教程 www.runoob.com www.w3cschool.cc www.runnoob.com 以上三個(gè)地址都能訪問""" |
| 變量 | 加final聲明常量 | var 變量 val 常量 延遲加載 | 延遲加載:變量?jī)H在使用時(shí)賦值。 lazy val helloString="Hello Crazy World" |
| 變量定義 | public String a; | var a String ; 支持多個(gè)變量聲明。 | Scala類型在變量后。 val xmax, ymax = 100 ?// xmax, ymax都聲明為100 |
| 訪問修飾符 | private (成員默認(rèn)) protected public | private ? private[x] 或 protected[x] | Scala protected更嚴(yán)格,僅對(duì)子類可見,同包類不可見。 private[x],讀作"這個(gè)成員除了對(duì)[…]中的類或[…]中的包中的類及它們的伴生對(duì)像可見外,對(duì)其它所有類都是private。 |
| 算術(shù)運(yùn)算符 | / | / | ? |
| 關(guān)系運(yùn)算符 | / | / | ? |
| 邏輯運(yùn)算符 | / | / | ? |
| 位運(yùn)算符 | / | / | ? |
| 賦值運(yùn)算符 | / | / | ? |
| 分支語句 | / | / | ? |
| 循環(huán)語句 | for( Type a? :? ?list) { ... } ? for(? ;? ?;???){ | for(var a? <-? ?list) { ... } for( a <- 1 to 10){ for( a <- numList | 區(qū)間 1 until 10 1 to 10 ? 過濾 for 使用 yield 將 for 循環(huán)的返回值作為一個(gè)變量存儲(chǔ)。語法格式如下: var retVal = for{ var x <- List ? |
| 數(shù)組 | int[] a = new ArrayList<String>(3); | var z:Array[String] = new Array[String](3) | val myMatrix = Array.ofDim[Int](3, 4) 多維數(shù)組。 |
| case | 常量,字符串 | 所有類型 | ?person match { ? ? ? case Person("Alice", 25) => println("Hi Alice!") ? ? ? case Person("Bob", 32) => println("Hi Bob!") ? ? ? case Person(name, age) => ? ? ? ? ?println("Age: " + age + " year, name: " + name + "?") ? ?} |
| 異常 | ? | ? | ?try { ? ? {
|
| 提取器(Extractor) ? | / | apply,unapply | 提取器是從傳遞給它的對(duì)象中提取出構(gòu)造該對(duì)象的參數(shù)。 ? ?def apply(user: String, domain: String) = { ? ?// 提取方法(必選) |
類和對(duì)象
類定義
//采用關(guān)鍵字class定義 class Person {//類成員必須初始化,否則會(huì)報(bào)錯(cuò)//這里定義的是一個(gè)公有成員var name:String=null }?默認(rèn)會(huì)為成員生成getter,setter方法
public class cn.scala.xtwy.Person {private java.lang.String name;public java.lang.String name();public void name_$eq(java.lang.String);public cn.scala.xtwy.Person(); }可以自定義getter,setter
class Person{//定義私有成員private var privateName:String=null;//getter方法def name=privateName//setter方法def name_=(name:String){this.privateName=name}}?如果也需要程序自動(dòng)會(huì)生成getter方法和setter方法,則需要引入 scala.reflect.BeanProperty然后采用注解的方式修飾變量
class Person {//類成員必須初始化,否則會(huì)報(bào)錯(cuò)//@BeanProperty用于生成getXxx,setXxx方法@BeanProperty var name:String="john" }?構(gòu)造函數(shù)
主構(gòu)造器的定義與類的定義交織在一直,將構(gòu)造器參數(shù)直接放在類名稱之后。
主構(gòu)造器的參數(shù),會(huì)變成類的成員。
//具有主構(gòu)建函數(shù)和輔助構(gòu)建函數(shù)的Person類 class Person(var name:String,var age:Int){//類成員private var sex:Int=0//輔助構(gòu)造器def this(name:String,age:Int,sex:Int){this(name,age)this.sex=sex} }單例對(duì)象
在某些應(yīng)用場(chǎng)景下,我們可能不需要?jiǎng)?chuàng)建對(duì)象,而是想直接調(diào)用方法,但是Scala語言并不支持靜態(tài)成員,Scala通過單例對(duì)象來解決該問題。
?
object Student {private var studentNo:Int=0;def uniqueStudentNo()={studentNo+=1studentNo}def main(args: Array[String]): Unit = {println(Student.uniqueStudentNo())} }伴生對(duì)象與伴生類?
其實(shí)伴生對(duì)象與伴生類本質(zhì)上是不同的兩個(gè)類,只不過伴生類與伴生對(duì)象之間可以相互訪問到對(duì)主的成員包括私有的成員變量或方法。
class Student(var name:String,age:Int)object Student {private var studentNo:Int=0;def uniqueStudentNo()={studentNo+=1studentNo}def main(args: Array[String]): Unit = {println(Student.uniqueStudentNo())} }apply方法?
通過利用apply方法可以直接利用類名創(chuàng)建對(duì)象
//定義Student類,該類稱為伴生類,因?yàn)樵谕粋€(gè)源文件里面,我們還定義了object Student class Student(var name:String,var age:Int){private var sex:Int=0//直接訪問伴生對(duì)象的私有成員def printCompanionObject()=println(Student.studentNo)}//伴生對(duì)象 object Student {private var studentNo:Int=0;def uniqueStudentNo()={studentNo+=1studentNo}//定義自己的apply方法。此處有new 關(guān)鍵字def apply(name:String,age:Int)=new Student(name,age)def main(args: Array[String]): Unit = {println(Student.uniqueStudentNo())val s=new Student("john",29)//直接訪問伴生類Student中的私有成員println(s.sex)//直接利用類名進(jìn)行對(duì)象的創(chuàng)建,這種方式實(shí)際上是調(diào)用前面的apply方法進(jìn)行實(shí)現(xiàn),這種方式的好處是避免了自己手動(dòng)new去創(chuàng)建對(duì)象 //此處沒有new,直接用類名當(dāng)方法名。val s1=Student("john",29)println(s1.name)println(s1.age)} }包
包定義
包即可以定義在頂部,也可以使用 c#方式定義包:
package cn{package scala{package xtwy{class Teacher {}}} }包的作用域與引入(import)的使用方法?
scala允許在任何地方進(jìn)行包的引入,_的意思是引入該包下的所有類和對(duì)象
package cn{package scala{//在包c(diǎn)n.scala下創(chuàng)建了一個(gè)Utils單例object Utils{def toString(x:String){println(x)}//外層包無法直接訪問內(nèi)層包,下面這一行代碼編譯通不過//如果一定要使用的話,可以引入包import cn.scala.xtwy._def getTeacher():Teacher=new Teacher("john")}//定義了cn.scala.xtwypackage xtwy{class Teacher(var name:String) {//演示包的訪問規(guī)則//內(nèi)層包可以訪問外層包中定義的類或?qū)ο?#xff0c;無需引入def printName()={Utils.toString(name)}}}} }訪問控制
在java語言中,主要通過public、private、protected及默認(rèn)控制來實(shí)現(xiàn)包中類成員的訪問控制,當(dāng)定義一個(gè)類時(shí),如果類成員不加任何訪問控制符時(shí),表示該類成員在定義該類的包中可見。在scala中沒有public關(guān)鍵字,僅有private 和 protected訪問控制符,當(dāng)一個(gè)類成員不加private和protected時(shí),它的訪問權(quán)限就是public。
?
| 無任何修飾符 | 任何地方都可以使用 |
| private[scala] | 在定義的類中可以訪問,在scala包及子包中可以訪問 |
| private[this] | 只能在定義的類中訪問,即使伴生對(duì)象也不能訪問團(tuán) |
| private | 在定義的的類及伴生對(duì)象中可以訪問,其它地方不能訪問 |
| protected[scala] | 在定義的類及子類中可以訪問,在scala包及子包中可以訪問, |
| protected[this] | 只能在定義的類及子類中訪問,即使伴生對(duì)象也不能訪問 |
| protected | 在定義的類及子類中訪問,伴生對(duì)象可以訪問,其它地方不能訪問 |
包對(duì)象
包對(duì)象主要用于將常量、工具函數(shù),使用時(shí)直接通過包名引用,類似Java的 static import。
package cn.scala.xtwy//利用package關(guān)鍵字定義單例對(duì)象 package object Math {val PI=3.141529val THETA=2.0val SIGMA=1.9 }class Coputation{def computeArea(r:Double)=Math.PI*r*r }?import高級(jí)特性
隱式引入
如果不引入任何包,scala會(huì)默認(rèn)引入
- java.lang._
- scala._
- Predef._
包中或?qū)ο笾兴械念惡头椒?/p>
重命名
scala中允許對(duì)引入的類或方法進(jìn)行重命名,如果我們需要在程序中同時(shí)使用java.util.HashMap及scala.collection.mutable.HashMap時(shí),可以利用重命名的方法消除命名沖突的問題,雖然也可以采用包名前綴的方式使用,但代碼不夠簡(jiǎn)潔
//將java.util.HashMap重命名為JavaHashMap import java.util.{ HashMap => JavaHashMap } import scala.collection.mutable.HashMapval javaHashMap = new JavaHashMap[String, String]()類隱藏?
//通過HashMap=> _,這樣類便被隱藏起來了 import java.util.{HashMap=> _,_}類層次結(jié)構(gòu)
層次結(jié)構(gòu)中,處于繼承層次最頂層的是Any類,它是scala繼承的根類,scala中所有的類都是它的子類。
//==與!=被聲明為final,它們不能被子類重寫 final def ==(that: Any): Boolean final def !=(that: Any): Boolean def equals(that: Any): Boolean def hashCode: Int def toString: String根類Any有兩個(gè)子類,它們分別是AnyVal和AnyRef。
AnyVal
其中AnyVal是所有scala內(nèi)置的值類型( Byte, Short, Char, Int, Long, Float, Double, Boolean, Unit.)的父類。
其中 Byte, Short, Char, Int, Long, Float, Double, Boolean與java中的byte,short,char,int,long,float,double,boolean原生類型對(duì)應(yīng),
而Unit對(duì)應(yīng)java中的void類型。()可以作為Unit類型的實(shí)例,它同樣可以調(diào)用toString等方法
由于( Byte, Short, Char, Int, Long, Float, Double, Boolean, Unit)繼承AnyVal,而AnyVal又繼承Any,因此它們也可以調(diào)用toString等方法。?
AnyRef
AnyRef是Any的另外一個(gè)子類,它是scala中所有非值類型的父類,對(duì)應(yīng)Java.lang.Object類(可以看作是java.lang.Object類的別名),也即它是所有引用類型的父類(除值類型外)。那為什么不直接Java.lang.Object作為scala非值引用類型的父類呢?這是因?yàn)镾cala還可以運(yùn)行在其它平臺(tái)上如.Net,所以它使用了AnyRef這個(gè)類,在JVM上它對(duì)應(yīng)的是java.lang.Object,而對(duì)于其它平臺(tái)有不同的實(shí)現(xiàn)。
Scala中原生類型的實(shí)現(xiàn)方式
scala采用與java相同原生類型存儲(chǔ)方式,由于性能方面及與java進(jìn)行操作方面的考慮,scala對(duì)于原生類型的基本操作如加減乘除操作與java是一樣的,當(dāng)需要遇到其他方法調(diào)用時(shí),則使用java的原生類型封裝類來表示,如Int類型對(duì)應(yīng)于java.lang.Integer類型,這種轉(zhuǎn)換對(duì)于我們使用者來說是透明的。
scala中的==操作它不區(qū)分你是原生類型還是引用類型
如果是在java語言中,它返回的是false。在scala中,對(duì)于原生類型,這種等于操作同java原生類型,而對(duì)于引用類型,它實(shí)際上是用equals方法對(duì)==方法進(jìn)行實(shí)現(xiàn),這樣避免了程序設(shè)計(jì)時(shí)存在的某些問題。那如果想判斷兩個(gè)引用類型是否相等時(shí)怎么辦呢? AnyRef中提供了eq、ne兩個(gè)方法用于判斷兩個(gè)引用是否相等
Nothing、Null類型
Null類型是所有AnyRef類型的子類型,也即它處于AnyRef類的底層,對(duì)應(yīng)java中的null引用。而Nothing是scala類中所有類的子類,它處于scala類的最底層。必須注意的是Null類型處于AnyRef類的底層,它不能夠作為值類型的子類
特質(zhì)(Traits)
Traits幾種不同使用方式
Trait類似Java接口,但java接口有其自身的局限性:接口中只能包括抽象方法,不能包含字段、具體方法。Scala語言利用Trait解決了該問題,在scala的trait中,它不但可以包括抽象方法還可以包含字段和具體方法。Trait更像抽象類。
注意:JDK1.8之后接口中可以定義默認(rèn)方法,靜態(tài)方法。
1、當(dāng)做java接口使用
2、帶具體實(shí)現(xiàn)的trait。
3、帶抽象字段的trait。字段未賦值。
4、具體字段的trait。字段賦值。
trait構(gòu)造順序
與Java接口,抽象類類似。
class Person class Student extends Person with FileLogger with Cloneable 上述構(gòu)造器的執(zhí)行順序?yàn)? 1 首先調(diào)用父類Person的構(gòu)造器 2 調(diào)用父trait Logger的構(gòu)造器 3 再調(diào)用trait FileLogger構(gòu)造器,再然后調(diào)用Cloneable的構(gòu)造器 4 最后才調(diào)用Student的構(gòu)造器trait與類的比較
trait有自己的構(gòu)造器,它是無參構(gòu)造器,不能定義trait帶參數(shù)的構(gòu)造器。除此之外 ,trait與普通的scala類并沒有其它區(qū)別。
//不能定義trait帶參數(shù)的構(gòu)造器 trait FileLogger(msg:String)?trait可以擴(kuò)展(extends)類
trait Logger{def log(msg:String):Unit } //Exception 是個(gè)class。 trait ExceptionLogger extends Exception with Logger{def log(msg:String):Unit={println(getMessage())} }self type?
即給自己(this)定義個(gè)別名。?
class A{//下面 self => 定義了this的別名,它是self type的一種特殊形式。//這里的self并不是關(guān)鍵字,可以是任何名稱self => val x=2 //可以用self.x作為this.x使用def foo = self.x + this.x }class OuterClass { outer => //定義了一個(gè)外部類別名val v1 = "here"class InnerClass {// 用outer表示外部類,相當(dāng)于OuterClass.thisprintln(outer.v1) } }trait X{} class B{//self:X => 要求B在實(shí)例化時(shí)或定義B的子類時(shí)//必須混入指定的X類型,這個(gè)X類型也可以指定為當(dāng)前類型self:X=> }//類C擴(kuò)展B的時(shí)候必須混入trait X //否則的話會(huì)報(bào)錯(cuò) class C extends B with X{def foo()=println("self type demo") }object SelfTypeDemo extends App{println(new C().foo) }函數(shù)式編程
函數(shù)式編程語言應(yīng)支持以下特性:
(1)高階函數(shù)(Higher-order functions)
(2)閉包( closures)
(3)模式匹配( Pattern matching)
(4)單一賦值( Single assignment )
(5)延遲計(jì)算( Lazy evaluation)
(6)類型推導(dǎo)( Type inference )
(7)尾部調(diào)用優(yōu)化( Tail call optimization)
參考:https://www.cnblogs.com/yinzhengjie/p/9370898.html
?
函數(shù)定義
return 可省略,返回值可省略,能自動(dòng)推導(dǎo)。
匿名函數(shù)
Array(1,2,3,4).map((x:Int)=>x+1 //匿名函數(shù) ).mkString(",")//花括方式 Array(1,2,3,4).map{(x:Int)=>x+1}.mkString(",") //省略.的方式 Array(1,2,3,4) map{(x:Int)=>x+1} mkString(",") //參數(shù)類型推斷寫法 Array(1,2,3,4) map{(x)=>x+1} mkString(",") //函數(shù)只有一個(gè)參數(shù)的話,可以省略() Array(1,2,3,4) map{x=>x+1} mkString(",") //如果參數(shù)右邊只出現(xiàn)一次,則可以進(jìn)一步簡(jiǎn)化 (—— 代表參數(shù)) Array(1,2,3,4) map{ _ + 1} mkString(",")//值函數(shù)簡(jiǎn)化方式 (_ 代表參數(shù)) scala> val fun1=1 + ( _ : Double ) val fun2 : (Double)=> Double = 1 + _函數(shù)參數(shù)
//函數(shù)參數(shù)(高階函數(shù)) //( (Int) => String ) => String scala> def convertIntToString( f : (Int) => String ) = f(4)//高階函數(shù)可以產(chǎn)生新的函數(shù) //(Double) => ( (Double) => Double ) scala> def multiplyBy(factor : Double) = ( x : Double ) => factor * x高階函數(shù)
高階函數(shù)主要有兩種:
- 將一個(gè)函數(shù)當(dāng)做另外一個(gè)函數(shù)的參數(shù)(即函數(shù)參數(shù))
- 返回值是函數(shù)的函數(shù)
常用高階函數(shù)
def map[B](f: (A) ? B): Array[B] .flatMap (x=>x.map(y=>y)) List("List","Set","Array") .filter (_.length>3) Array(1,2,4,3,5).reduce(_+_) Array(1,2,4,3,5) .foldLeft(0) ((x:Int,y:Int)=>{println(x,y);x+y}) Array(1,2,4,3,5) .foldRight(0) ((x:Int,y:Int)=>{println(x,y);x+y}) Array(1,2,4,3,5) .scanLeft(0) ((x:Int,y:Int)=>{println(x,y);x+y})SAM(simple abstract method)轉(zhuǎn)換?
一個(gè)trait(或者抽象類)只有一個(gè)方法,在作為參數(shù)傳遞時(shí),即可以通過匿名類傳遞參數(shù),也可以只傳遞一個(gè)函數(shù)。類似于Java的?@FunctionalInterface。
button.addActionListener((event:ActionEvent)=>counter+=1) button.addActionListener(new ActionListener{override def actionPerformed(event:ActionEvent){counter+=1} })new ActionListener{override def actionPerformed(event:ActionEvent){}函數(shù)柯里化
//mutiplyBy這個(gè)函數(shù)的返回值是一個(gè)函數(shù) //該函數(shù)的輸入是Doulbe (參數(shù)x),返回值也是Double def multiplyBy(factor:Double) = (x:Double) => factor*x//返回的函數(shù)作為值函數(shù)賦值給變量x val x=multiplyBy(10)//變量x現(xiàn)在可以直接當(dāng)函數(shù)使用 x(50)函數(shù)柯里化(curry)是怎么樣的呢?其實(shí)就是將multiplyBy函數(shù)定義成如下形式
// x:是返回函數(shù)的參數(shù) def multiplyBy(factor:Double) (x:Double) = x * factor multiplyBy(10)(50)//但此時(shí)它不能像def multiplyBy(factor:Double)=(x:Double)=>factor*x函數(shù)一樣,可以輸入單個(gè)參數(shù)進(jìn)行調(diào)用。會(huì)出錯(cuò)。 multiplyBy(10) //錯(cuò)誤提示函數(shù)multiplyBy缺少參數(shù),如果要這么做的話,需要將其定義為偏函數(shù) //Double => Double = <function1> ,返回一個(gè)函數(shù),輸入double,輸出double。 multiplyBy(10) _?部分應(yīng)用函數(shù)
那什么是部分應(yīng)用函數(shù)呢,所謂部分應(yīng)用函數(shù)就是指,當(dāng)函數(shù)有多個(gè)參數(shù),而在我們使用該函數(shù)時(shí)我們不想提供所有參數(shù)(假設(shè)函數(shù)有3個(gè)函數(shù)),只提供0~2個(gè)參數(shù),此時(shí)得到的函數(shù)便是部分應(yīng)用函數(shù)。
下劃線 _ 并不是占位符的作用,而是作為部分應(yīng)用函數(shù)的定義符
//定義一個(gè)求和函數(shù) scala> def sum(x:Int,y:Int,z:Int)=x+y+z sum: (x: Int, y: Int, z: Int)Int//不指定任何參數(shù)的部分應(yīng)用函數(shù) scala> val s1=sum _ s1: (Int, Int, Int) => Int = <function3>scala> s1(1,2,3) res91: Int = 6//指定兩個(gè)參數(shù)的部分應(yīng)用函數(shù) scala> val s2=sum(1,_:Int,3) s2: Int => Int = <function1>scala> s2(2) res92: Int = 6//指定一個(gè)參數(shù)的部分應(yīng)用函數(shù) scala> val s3=sum(1,_:Int,_:Int) s3: (Int, Int) => Int = <function2>scala> s3(2,3) res93: Int = 6?
總結(jié)
以上是生活随笔為你收集整理的Scala语言整理(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring AOP源码解析(三)——
- 下一篇: docker进阶