日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Scala 类型的类型(一)

發(fā)布時間:2025/1/21 编程问答 12 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Scala 类型的类型(一) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • 1. Scala 類型的不同類型
  • 2. 寫作進度
  • 3. Type Ascription
  • 4. 通用類型系統(tǒng) — Any, AnyRef, AnyVal
  • 5. 底類型 - Nothing Null

1. Scala 類型的不同類型

2013 年在幾場 JavaOne 大會」之后,掀起了一些關(guān)于 Scala 類型」方面的熱議,這篇博文也應(yīng)運而生。

在這些討論聲中,我發(fā)現(xiàn)不同的人在學(xué)習(xí) Scala 的過程中,經(jīng)常重復(fù)提出相同的問題。我想我們?nèi)鄙僖粋€詳盡的清單,來指明跟 Scala 類型打交道的方法,所以我決定總結(jié)下自己的經(jīng)驗,分享在 Scala 中為什么我們需要這些類型。

2. 寫作進度

盡管我寫這篇文章已經(jīng)有段時間了,但始終還有很多內(nèi)容未完成。比如說「高階類型」部分需要重新梳理,「Self Type」還得補充更多細節(jié),等等等等。詳情參見計劃清單。

此外,如果你看到某個部分被打上了 ? ,則表示該部分需要修改或者是未完成。

3. Type Ascription

Scala 有「類型推導(dǎo)」,這意味著我們可以在源碼中省略一些類型聲明。在不顯式聲明類型的前提下,我們只要書寫?val?def?就夠了。

這種顯式指定類型的行為,被稱為 Type Ascription(有時候,也有叫作 Type Annotation,但這個名字很容易造成混淆,在 Scala 文檔中并不這么使用)。

1

2

3

4

5

6

7

8

trait Thing

def getThing = new Thing { }

// without Type Ascription, the type is infered to be `Thing`

val infered = getThing

// with Type Ascription

val thing: Thing = getThing

在此類情況下,我們可以不使用 Type Ascription 。當然你也可以針對每個公有的方法顯示聲明返回類型(一個非常好的習(xí)慣),這能使讓代碼可讀性更好。

你可以根據(jù)以下的提示問題,來決定是否使用 Type Ascription

Q: 如果它是一個參數(shù)?

A: 必須使用。

Q: 如果它是一個公有方法的返回值?

A: 為了更好的代碼可讀性,及輸出類型的可控性,需要使用。

Q: 如果它是一個遞歸或重載的方法?

A: 必須使用。

Q: 當你需要返回一個比隱式推導(dǎo)結(jié)果更通用的接口?

A: 除非你愿意暴露實現(xiàn)細節(jié),否則必須使用。

除上述情況之外,則可以不必顯式聲明類型。

補充說明:

使用 Type Ascription 可以加快編譯的速度,通常我們也很樂意看到一個方法的返回類型。

好了,我們現(xiàn)在明白了 Type Ascription 大概是怎么一回事。講完這個之后,我們繼續(xù)接下來的話題,類型隨之也會變得越來越有趣。

4. 通用類型系統(tǒng) — Any, AnyRef, AnyVal

我們之所以說 Scala 的類型系統(tǒng)是通用的,是因為有一個「頂類型」—?Any?。這與 Java 很不一樣,后者存在叫做「原始類型」 (?int?,?long?,?float?,?double?,?byte?,?char?,?short?,?boolean?) 的特例,它們并不繼承 Java 中類似頂類型的東西?java.lang.Object


Scala 引入了?Any?作為所有類型共同的頂類型。Any??AnyRef??AnyVal?的超類。

AnyRef?面向 JavaJVM)的對象世界,它對應(yīng)?java.lang.Object?,是所有對象的超類。

AnyVal?則代表了 Java 的值世界,例如?int?以及其它 JVM 原始類型。

正是依賴這種繼承設(shè)計,我們才能夠使用?Any?定義方法,同時兼容?scala.int?以及?java.lang.String?的實例。

1

2

3

4

5

6

7

8

9

10

class Person

val allThings = ArrayBuffer[Any]()

val myInt = 42 // Int, kept as low-level `int` during runtime

allThings += myInt // Int (extends AnyVal)

// has to be boxed (!) -> becomes java.lang.Integer in the collection (!)

allThings += new Person() // Person (extends AnyRef), no magic here

雖然在 JVM 層一旦遭遇?ArrayBuffer[Any]?,我們的 Int 實例就會被打包成對象。對于類型系統(tǒng)而言,這一切還算是透明的。我們可以通過 Scala REPL ?:javap?來調(diào)查下上述的例子,這樣子可以找到我們的測試類產(chǎn)生的代碼。

1

2

3

35: invokevirtual #47 // Method myInt:()I

38: invokestatic #53 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;

41: invokevirtual #57 // Method scala/collection/mutable/ArrayBuffer.$plus$eq:(Ljava/lang/Object;)Lscala/collection/mutable/ArrayBuffer;

你將注意到?myInt?起初還是攜帶一個原始?int?類型的值。然后,在它即將被添加到?ArrayBuffer?的時候,scalac 植入了一個方法?BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer?(提醒下不是經(jīng)常跟「字節(jié)碼」打交道的讀者,這個方法就是?public Integer boxToInteger(i: int))。

通過這么一個智能的編譯器,以及在這套公共繼承體系中將所有東西都當成一個對象來處理,我們就能夠擺脫「原始類型」這種邊緣情況的糾纏,至少在我們的 Scala 源碼中,編譯器會為我們處理它。

當然在 JVM 層面,這種差異依舊存在。由于「原始類型」的操作更安全,同時占用更少的內(nèi)存(對象明顯要占用更多),scalac 會在盡可能的情況下使用原始類型。

另一方面,我們也可以限制一個方法只能采用輕量級的值類型:

1

2

3

4

5

6

def check(in: AnyVal) = ()

check(42) // Int -> AnyVal

check(13.37) // Double -> AnyVal

check(new Object) // -> AnyRef = fails to compile

在上述例子中,我們使用了一個 TypeClass?Checker[T]?與類型邊界 (type bound)(后續(xù)會詳談)。總體思路就是這個方法只能采用 Value Classes ,如 Int 或我們自己的值類型。雖然這不是慣用的方法,但這展示了 Scala 的類型系統(tǒng)如何擁抱 Java 的原始類型,把它們引入到 "真正的" 類型系統(tǒng)里面,而不是像 Java 一樣,僅僅將它們作為一個分離的情況存在。

5. 底類型 - Nothing Null

Scala 中,一切皆有類型…… 但你是否想過,當遇到一些非正常的情況,比如拋出異常的時候,類型推導(dǎo)是如何保持正常運轉(zhuǎn),推斷出合理的類型。

讓我們通過以下的?if/else throw?的例子來一探究竟:

1

2

3

4

5

val thing: Int =

if (test)

42 // : Int

else

throw new Exception("Whoops!") // : Nothing

正如你在注釋里所看到的,if?塊的返回類型是?Int(很明顯),else?代碼塊的類型是?Nothing(有點意思)。推導(dǎo)器之所以能夠推斷?thing?的類型將永遠是?Int,主要是?Nothing?類型的「底類型」性質(zhì)在起作用。

一個關(guān)于「底類型」如何運作的準確直覺是:Nothing 繼承了所有類型。

類型推導(dǎo)總是會尋找?if?語句兩個邏輯分支的「共同類型」。因此如果?else?分支這里是一個繼承所有類型的子類型,那么最終推斷出來的結(jié)果自然會是第一個分支的類型。

1

2

3

4

Types visualized:

[Int] -> ... -> AnyVal -> Any

Nothing -> [Int] -> ... -> AnyVal -> Any

同樣的道理也適用于 Scala 中的第二個底類型 -?Null?

1

2

3

4

5

val thing: String =

if (test)

"Yay!" // : String

else

????null // : Null

thing?的類型是預(yù)期的?String?Null?遵循著跟?Nothing?幾乎一樣的規(guī)則。我將通過這個例子先探討下類型推導(dǎo)中?AnyVal??AnyRef?之間的區(qū)別。

1

2

3

4

5

6

Types visualized:

[String] -> AnyRef -> Any

Null -> [String] -> AnyRef -> Any

infered type: String

讓我們考慮下?Int?及其它不能兼容?Null?值的原始類型。我們在 REPL 中使用?:type?命令來調(diào)查這個情況(這樣可以返回一個表達式的類型)。

1

2

scala> :type if (false) 23 else null

Any

這跟上面一個分支對象為?String?類型的例子不同。因為?Null?不像?Nothing?一樣繼承任何類型,我們來詳細研究一下這里的類型。讓我們再次使用?:type?命令來看看?Int?到底繼承了什么:

1

2

3

4

5

6

scala> :type -v 12

// Type signature

Int

// Internal Type structure

TypeRef(TypeSymbol(final abstract class Int extends AnyVal))

verbose?參數(shù)在這里新增了一些信息,現(xiàn)在我們知道了?Int? 一個?AnyVal,后者是個特殊的用于表示值類型的?class,它不能兼容?Null。如果我們看?AnyVal?的源碼,我們將發(fā)現(xiàn):

1

abstract class AnyVal extends Any with NotNull

我之所以要講是這里,是因為?AnyVal?的核心功能在這里通過類型很好地表示出來了。注意那個?NotNull?特質(zhì)(trait

回到主題,為什么上面?if?語句(兩個邏輯分支的類型分別是?AnyVal??null)的公共類型是?Any,而不是其它。

用一句話來總結(jié)就是:

Null 繼承所有的 AnyRefs,而 Nothing 繼承了一切。

由于 AnyVals (例如數(shù)字)跟 AnyRefs 并不在一個繼承樹中,一個數(shù)字與一個?null?值唯一的公共類型就是?Any?,這就解釋了我們的例子。

1

2

3

4

5

6

Types visualized:

Int -> NotNull -> AnyVal -> [Any]

Null -> AnyRef -> [Any]

infered type: Any an object

總結(jié)

以上是生活随笔為你收集整理的Scala 类型的类型(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。