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