Scala入門到精通——第十節 Scala類層次結構、Traits初步
本節主要內容
- Scala類層次結構總覽
- Scala中原生型別的實現方式解析
- Nothing、Null型別解析
- Traits簡介
- Traits幾種不同使用方式
1 Scala類層次結構
Scala中的類層次結構圖如下:
來源:Programming in Scala
從上面的類層次結構圖中可以看到,處於繼承層次最頂層的是Any類,它是scala繼承的根類,scala中所有的類都是它的子類
Any類中定義了下面幾個方法:
//==與!=被宣告為final,它們不能被子類重寫
final def ==(that: Any): Boolean
final def !=(that: Any): Boolean
def equals(that: Any): Boolean
def hashCode: Int
def toString: String
從上面的程式碼看可以看到,Any類中共包括了五個方法,其中==與!=被宣告為final型別的,因此它們不能被子類重寫,事實上==的真正實現是通過equals方法來實現的,而!=是通過!equals來實現的,因此如果想改變==與!=方法的行為的話,可以直接對equals進行重寫。
根類Any有兩個子類,它們分別是AnyVal和AnyRef,其中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型別,由於( Byte, Short, Char, Int, Long, Float, Double, Boolean, Unit)繼承AnyVal,而AnyVal又繼承Any,因此它們也可以呼叫toString等方法。
scala> 2.0.hashCode
res5: Int = 1073741824
scala> 2.0 toString
res6: String = 2.0
值得一提的是,()可以作為Unit型別的例項,它同樣可以呼叫toString等方法
scala> ().hashCode
res7: Int = 0
scala> ().toString
res8: String = ()
scala> ()==()
<console>:8: warning: comparing values of types Unit and Unit using `==' will al
ways yield true
()==()
^
res9: Boolean = true
AnyRef是Any的另外一個子類,它是scala中所有非值型別的父類,對應Java.lang.Object類(可以看作是java.lang.Object類的別名),也即它是所有引用型別的父類(除值型別外)。那為什麼不直接Java.lang.Object作為scala非值引用型別的父類呢?這是因為Scala還可以執行在其它平臺上如.Net,所以它使用了AnyRef這個類,在JVM上它對應的是java.lang.Object,而對於其它平臺有不同的實現。
2 Scala中原生型別的實現方式解析
scala採用與java相同原生型別儲存方式,由於效能方面及與java進行操作方面的考慮,scala對於原生型別的基本操作如加減乘除操作與java是一樣的,當需要遇到其他方法呼叫時,則使用java的原生型別封裝類來表示,如Int型別對應於java.lang.Integer型別,這種轉換對於我們使用者來說是透明的。
在本課程的第二節中我們提到,scala中的==操作它不區分你是原生型別還是引用型別,例如
scala> "abc"=="abc"
res10: Boolean = true
如果是在java語言中,它返回的是false。在scala中,對於原生型別,這種等於操作同java原生型別,而對於引用型別,它實際上是用equals方法對==方法進行實現,這樣避免了程式設計時存在的某些問題。那如果想判斷兩個引用型別是否相等時怎麼辦呢? AnyRef中提供了eq、ne兩個方法用於判斷兩個引用是否相等,如
scala> val x=new String("123")
x: String = 123
scala> val y=new String("123")
y: String = 123
scala> x==y
res15: Boolean = true
scala> x.eq(y)
res16: Boolean = false
scala> x ne y
res17: Boolean = true
3 Nothing、Null型別解析
在前面的類層次結構圖中可以看到,Null型別是所有AnyRef型別的子型別,也即它處於AnyRef類的底層,對應java中的null引用。而Nothing是scala類中所有類的子類,它處於scala類的最底層。
這裡面必須注意的是Null型別處於AnyRef類的底層,它不能夠作為值型別的子類,例如:
scala> var x:Int=null
<console>:7: error: type mismatch;
found : Null(null)
required: Int
Note that implicit conversions are not applicable because they are ambiguous:
both method Integer2intNullConflict in class LowPriorityImplicits of type (x: N
ull)Int
and method Integer2int in object Predef of type (x: Integer)Int
are possible conversion functions from Null(null) to Int
var x:Int=null
^
Nothing這個類一般用於指示程式返回非正常結果,利用Nothing作為返回值可以增加程式的靈活性。例如:
def error(msg:String):Nothing={
throw new RuntimeException(msg)
}
def divide(x: Int, y: Int): Int =
if (y != 0) x / y
else error("can't divide by zero")
4 Traits簡介
scala和java語言一樣,採用了很強的限制策略,避免了多種繼承的問題。在java語言中,只允許繼承一個超類,該類可以實現多個介面,但java介面有其自身的侷限性:介面中只能包括抽象方法,不能包含欄位、具體方法。Scala語言利用Trait解決了該問題,在scala的trait中,它不但可以包括抽象方法還可以包含欄位和具體方法。trait的示例如下:
//trait定義演示
trait DAO{
//定義一個抽象方法,注意不需要加abstract
//加了abstract反而會報錯
def delete(id:String):Boolean
def add(o:Any):Boolean
def update(o:Any):Int
def query(id:String):List[Any]
}
生成的位元組碼檔案反編譯後的結果:
D:\ScalaWorkspace\ScalaChapter10\bin\cn\scala\xtwy>javap -private DAO.class
Compiled from "Dao.scala"
public interface cn.scala.xtwy.DAO {
public abstract boolean delete(java.lang.String);
public abstract boolean add(java.lang.Object);
public abstract int update(java.lang.Object);
public abstract scala.collection.immutable.List<java.lang.Object> query(java.l
ang.String);
}
下面的程式碼演示瞭如果使用trait
trait MysqlDAO{
def add(o:Any):Boolean
def update(o:Any):Int
def query(id:String):List[Any]
}
class DaoImpl extends MysqlDAO{
def add(o:Any):Boolean=true
def update(o:Any):Int= 1
def query(id:String):List[Any]=List(1,2,3)
}
如果有多個trait的話:
trait MysqlDAO{
var recodeMount:Long=15000000000000L
def add(o:Any):Boolean
def update(o:Any):Int
def query(id:String):List[Any]
}
class DaoImpl extends MysqlDAO with Cloneable{
def add(o:Any):Boolean=true
def update(o:Any):Int= 1
def query(id:String):List[Any]=List(1,2,3)
}
5 Traits幾種不同使用方式
1 當做java介面使用的trait,如
//trait定義演示
trait DAO{
//定義一個抽象方法,注意不需要加abstract
//加了abstract反而會報錯
def delete(id:String):Boolean
def add(o:Any):Boolean
def update(o:Any):Int
def query(id:String):List[Any]
}
2 帶具體實現的trait
trait DAO{
//delete方法有具體實現
def delete(id:String):Boolean={
println("delete implementation")
true
}
def add(o:Any):Boolean
def update(o:Any):Int
def query(id:String):List[Any]
}
這裡定義的特質將生成兩個位元組碼檔案:
D:\ScalaWorkspace\ScalaChapter10\bin\cn\scala\xtwy 的目錄
2015/07/25 22:20 <DIR> .
2015/07/25 22:20 <DIR> ..
2015/07/25 22:20 575 DAO$class.class
2015/07/25 22:20 898 DAO.class
2 個檔案 1,473 位元組
2 個目錄 175,333,232,640 可用位元組
D:\ScalaWorkspace\ScalaChapter10\bin\cn\scala\xtwy>javap -private DAO$class.class
Compiled from "Dao.scala"
public abstract class cn.scala.xtwy.DAO$class {
public static boolean delete(cn.scala.xtwy.DAO, java.lang.String);
public static void $init$(cn.scala.xtwy.DAO);
}
D:\ScalaWorkspace\ScalaChapter10\bin\cn\scala\xtwy>javap -private DAO.class
Compiled from "Dao.scala"
public abstract class cn.scala.xtwy.DAO$class {
public static boolean delete(cn.scala.xtwy.DAO, java.lang.String);
public static void $init$(cn.scala.xtwy.DAO);
}
從位元組碼檔案可以看出,帶有具體實現的trait是通過java中的抽象類來實現的。
3 帶抽象欄位的trait
trait DAO{
var recodeMount:Long
def delete(id:String):Boolean={
println("delete implementation")
true
}
def add(o:Any):Boolean
def update(o:Any):Int
def query(id:String):List[Any]
}
4 具體欄位的trait
trait DAO{
var recodeMount:Long=15000000000000L
def delete(id:String):Boolean={
println("delete implementation")
true
}
def add(o:Any):Boolean
def update(o:Any):Int
def query(id:String):List[Any]
}
新增公眾微訊號,可以瞭解更多最新Spark、Scala相關技術資訊