Julia 中,如果類(lèi)型被省略,則值可以是任意類(lèi)型。添加類(lèi)型會(huì)顯著提高性能和系統(tǒng)穩(wěn)定性。
Julia 類(lèi)型系統(tǒng)的特性是,具體類(lèi)型不能作為具體類(lèi)型的子類(lèi)型,所有的具體類(lèi)型都是最終的,它們可以擁有抽象類(lèi)型作為父類(lèi)型。其它高級(jí)特性有:
Julia 的類(lèi)型系統(tǒng)的設(shè)計(jì)旨在有效及具表現(xiàn)力,既清楚直觀又不夸張。許多 Julia 程序員可能永遠(yuǎn)不會(huì)覺(jué)得有必要去明確地指出類(lèi)型。然而某些程序會(huì)因聲明類(lèi)型變得更清晰,更簡(jiǎn)單,更迅速及健壯。
::
運(yùn)算符可以用來(lái)在程序中給表達(dá)式和變量附加類(lèi)型注釋。這樣做有兩個(gè)理由:
::
運(yùn)算符放在表示值的表達(dá)式之后時(shí)讀作“前者是后者的實(shí)例”,它用來(lái)斷言左側(cè)表達(dá)式是否為右側(cè)表達(dá)式的實(shí)例。如果右側(cè)是具體類(lèi)型,此類(lèi)型應(yīng)該是左側(cè)的實(shí)例。如果右側(cè)是抽象類(lèi)型,左側(cè)應(yīng)是一個(gè)具體類(lèi)型的實(shí)例的值,該具體類(lèi)型是這個(gè)抽象類(lèi)型的子類(lèi)型。如果類(lèi)型斷言為假,將拋出異常,否則,返回左值:
julia> (1+2)::FloatingPoint
ERROR: type: typeassert: expected FloatingPoint, got Int64
julia> (1+2)::Int
3
可以在任何表達(dá)式的所在位置做類(lèi)型斷言。 ::
最常見(jiàn)的用法是作為一個(gè)在函數(shù)/方法簽名中的斷言,例如 f(x::Int8) = ...
(查看方法)。
::
運(yùn)算符跟在表達(dá)式上下文中的變量名后時(shí),它聲明變量應(yīng)該是某個(gè)類(lèi)型,有點(diǎn)兒類(lèi)似于 C 等靜態(tài)語(yǔ)言中的類(lèi)型聲明。賦給這個(gè)變量的值會(huì)被 convert
函數(shù)轉(zhuǎn)換為所聲明的類(lèi)型:
julia> function foo()
x::Int8 = 1000
x
end
foo (generic function with 1 method)
julia> foo()
-24
julia> typeof(ans)
Int8
這個(gè)特性用于避免性能陷阱,即給一個(gè)變量賦值時(shí)意外更改了類(lèi)型。
“聲明”僅發(fā)生在特定的上下文中:
x::Int8 # a variable by itself
local x::Int8 # in a local declaration
x::Int8 = 10 # as the left-hand side of an assignment
并適用于整個(gè)當(dāng)前范圍,甚至在聲明之前。目前,聲明類(lèi)型不能用于全局范圍,例如在 REPL 中就不可以,因?yàn)?Julia 還沒(méi)有定型的全局變量。需要注意的是在函數(shù)返回語(yǔ)句中,上述的前兩個(gè)表達(dá)式計(jì)算值,還有就是 ::
是一個(gè)類(lèi)型的斷言不是一個(gè)聲明。
抽象類(lèi)型不能被實(shí)例化,它組織了類(lèi)型等級(jí)關(guān)系,方便程序員編程。如,編程時(shí)可針對(duì)任意整數(shù)類(lèi)型,而不需指明是哪種具體的整數(shù)類(lèi)型。
使用 abstract
關(guān)鍵字聲明抽象類(lèi)型:
abstract ?name?
abstract ?name? <: ?supertype?
abstract
關(guān)鍵字引入了新的抽象類(lèi)型,類(lèi)型名為 ?name?
。類(lèi)型名后可跟 <:
及已存在的類(lèi)型,表明新聲明的抽象類(lèi)型是這個(gè)“父”類(lèi)型的子類(lèi)型。
如果沒(méi)有指明父類(lèi)型,則父類(lèi)型默認(rèn)為 Any
——所有對(duì)象和類(lèi)型都是這個(gè)抽象類(lèi)型的子類(lèi)型。在類(lèi)型理論中,Any
位于類(lèi)型圖的頂峰,被稱(chēng)為“頂”。Julia 也有預(yù)定義的抽象“底”類(lèi)型,它位于類(lèi)型圖的最底處,被稱(chēng)為 None
。None
與 Any
對(duì)立:任何對(duì)象都不是 None
的實(shí)例,所有的類(lèi)型都是 None
的父類(lèi)型。
下面是構(gòu)造 Julia 數(shù)值體系的抽象類(lèi)型子集的具體例子:
abstract Number end
abstract Real <: Number
abstract AbstractFloat <: Real
abstract Integer <: Real
abstract Signed <: Integer
abstract Unsigned <: Integer
<:
運(yùn)算符意思為“前者是后者的子類(lèi)型”,它聲明右側(cè)是左側(cè)新聲明類(lèi)型的直接父類(lèi)型。也可以用來(lái)判斷左側(cè)是不是右側(cè)的子類(lèi)型:
julia> Integer <: Number
true
julia> Integer <: FloatingPoint
false
抽象類(lèi)型的一個(gè)重要用途是為具體的類(lèi)型提供默認(rèn)實(shí)現(xiàn)。舉個(gè)簡(jiǎn)單的例子:
function myplus(x, y)
x + y
endof
第一點(diǎn)需要注意的是, 上面的參數(shù)聲明等效于 x::Any
和 y::Any
. 當(dāng)這個(gè)函數(shù)被調(diào)用時(shí), 例如 myplus(2, 5)
, Julia 會(huì)首先查找參數(shù)類(lèi)型匹配的 myplus
函數(shù). (關(guān)于多重分派的詳細(xì)信息請(qǐng)參考下文.) 如果沒(méi)有找到比上面的函數(shù)更相關(guān)的函數(shù), Julia 根據(jù)上面的通用函數(shù)定義并編譯一個(gè) myplus
具體函數(shù), 其參數(shù)為兩個(gè) Int 型變量, 也就是說(shuō),
Julia 會(huì)定義并編譯:
function myplus(x::Int, y::Int)
x + y
end
最后, 調(diào)用這個(gè)具體的函數(shù)。
因此, 程序員可以利用抽象類(lèi)型編寫(xiě)通用的函數(shù),然后這個(gè)通用函數(shù)可以被許多具體的類(lèi)型組合調(diào)用。也正是由于多重分派,程序員可以精確的控制是調(diào)用更具體的還是通用的函數(shù)。
需要注意的一點(diǎn)是, 編寫(xiě)面向抽象類(lèi)型的函數(shù)并不會(huì)帶來(lái)性能上的損失,因?yàn)槊看握{(diào)用函數(shù)時(shí),根據(jù)不同的參數(shù)組合,函數(shù)總是要重新編譯的。(然而, 如果參數(shù)類(lèi)型為包含抽象類(lèi)型的容器是, 會(huì)有性能方面的問(wèn)題;參見(jiàn)下面的關(guān)于性能的提示。)
位類(lèi)型是具體類(lèi)型,它的數(shù)據(jù)是由位構(gòu)成的。整數(shù)和浮點(diǎn)數(shù)都是位類(lèi)型。標(biāo)準(zhǔn)的位類(lèi)型是用 Julia 語(yǔ)言本身定義的:
bitstype 16 Float16 <: FloatingPoint
bitstype 32 Float32 <: FloatingPoint
bitstype 64 Float64 <: FloatingPoint
bitstype 8 Bool <: Integer
bitstype 32 Char <: Integer
bitstype 8 Int8 <: Signed
bitstype 8 Uint8 <: Unsigned
bitstype 16 Int16 <: Signed
bitstype 16 Uint16 <: Unsigned
bitstype 32 Int32 <: Signed
bitstype 32 Uint32 <: Unsigned
bitstype 64 Int64 <: Signed
bitstype 64 Uint64 <: Unsigned
bitstype 128 Int128 <: Signed
bitstype 128 Uint128 <: Unsigned
聲明位類(lèi)型的通用語(yǔ)法是:
bitstype ?bits? ?name?
bitstype ?bits? ?name? <: ?supertype?
?bits?
表明類(lèi)型需要多少空間來(lái)存儲(chǔ),?name?
為新類(lèi)型的名字。目前,位類(lèi)型的聲明的位數(shù)只支持 8 的倍數(shù),因此布爾類(lèi)型也是 8 位的。
Bool
, Int8
及 Uint8
類(lèi)型的聲明是完全相同的,都占用了 8 位內(nèi)存,但它們是互相獨(dú)立的。
復(fù)合類(lèi)型也被稱(chēng)為記錄、結(jié)構(gòu)、或者對(duì)象。復(fù)合類(lèi)型是變量名域的集合。它是 Julia 中最常用的自定義類(lèi)型。在 Julia 中,所有的值都是對(duì)象,但函數(shù)并不與它們所操作的對(duì)象綁定。Julia 重載時(shí),根據(jù)函數(shù) 所有參數(shù)的類(lèi)型,而不僅僅是第一個(gè)參數(shù)的類(lèi)型,來(lái)選取調(diào)用哪個(gè)方法(詳見(jiàn) :方法 )。
使用 type
關(guān)鍵字來(lái)定義復(fù)合類(lèi)型:
julia> type Foo
bar
baz::Int
qux::Float64
end
構(gòu)建復(fù)合類(lèi)型 Foo
的對(duì)象:
julia> foo = Foo("Hello, world.", 23, 1.5)
Foo("Hello, world.",23,1.5)
julia> typeof(foo)
Foo (constructor with 2 methods)
當(dāng)一個(gè)類(lèi)型像函數(shù)一樣被調(diào)用時(shí),它可以被叫做類(lèi)型構(gòu)造函數(shù)(constructor)。每個(gè)類(lèi)型有兩種構(gòu)造函數(shù)是自動(dòng)被生成的(它們被叫做默認(rèn)構(gòu)造函數(shù))。第一種是當(dāng)傳給構(gòu)造函數(shù)的參數(shù)和這個(gè)類(lèi)型的字段類(lèi)型不一一匹配時(shí),構(gòu)造函數(shù)會(huì)把它的參數(shù)傳給 convert
函數(shù),并且轉(zhuǎn)換到這個(gè)類(lèi)型相應(yīng)的字段類(lèi)型。第二種是當(dāng)傳給構(gòu)造函數(shù)的每個(gè)參數(shù)和這個(gè)類(lèi)型的字段類(lèi)型都一一相同時(shí),構(gòu)造函數(shù)直接生成類(lèi)型。要自動(dòng)生成兩種默認(rèn)構(gòu)造函數(shù)的原因是:為了防止用戶在聲明別的新變量的時(shí)候不小心把構(gòu)造函數(shù)給覆蓋掉。
由于沒(méi)有約束 bar
的類(lèi)型,它可以被賦任意值;但是 baz
必須能被轉(zhuǎn)換為 Int
:
julia> Foo((), 23.5, 1)
ERROR: InexactError()
in Foo at no file
你可以用 names
這個(gè)函數(shù)來(lái)獲取類(lèi)型的所有字段。
julia> names(foo)
3-element Array{Symbol,1}:
:bar
:baz
:qux
獲取復(fù)合對(duì)象域的值:
julia> foo.bar
"Hello, world."
julia> foo.baz
23
julia> foo.qux
1.5
修改復(fù)合對(duì)象域的值:
julia> foo.qux = 2
2.0
julia> foo.bar = 1//2
1//2
沒(méi)有域的復(fù)合類(lèi)型是單態(tài)類(lèi)型,這種類(lèi)型只能有一個(gè)實(shí)例:
type NoFields
end
julia> is(NoFields(), NoFields())
true
is
函數(shù)驗(yàn)證 NoFields
的“兩個(gè)”實(shí)例是否為同一個(gè)。有關(guān)單態(tài)類(lèi)型,后面會(huì)詳細(xì)講。
有關(guān)復(fù)合類(lèi)型如何實(shí)例化,需要 參數(shù)化類(lèi)型和方法這兩個(gè)背景知識(shí)。將在構(gòu)造函數(shù)中詳細(xì)介紹構(gòu)造實(shí)例。
可以使用關(guān)鍵詞 immutable
替代 type
來(lái)定義 不可變 復(fù)合類(lèi)型:
immutable Complex
real::Float64
imag::Float64
end
這種類(lèi)型和其他復(fù)合類(lèi)型類(lèi)似,除了它們的實(shí)例不能被更改。不可變復(fù)合類(lèi)型具有以下幾種優(yōu)勢(shì):
Complex
例子里的類(lèi)型就被有效地封裝到數(shù)組里,而且有些時(shí)候編譯器能夠避免完整地分配不可變對(duì)象。一個(gè)不可變對(duì)象可以包含可變對(duì)象,比如數(shù)組,域。那些被包含的可變對(duì)象仍然保持可變;只有不可變對(duì)象自己的域不能變得指向別的對(duì)象。
理解不可變復(fù)合變量的一個(gè)有用的辦法是每個(gè)實(shí)例都是和特定域的值相關(guān)聯(lián)的 - 這些域的值就能告訴你關(guān)于這個(gè)對(duì)象的一切。相反地,一個(gè)可變的對(duì)象就如同一個(gè)小的容器可能包含了各種各樣的值,所以它不能從它的域的值確定出這個(gè)對(duì)象。在決定是否把一個(gè)類(lèi)型定義為不變的時(shí)候,先問(wèn)問(wèn)是否兩個(gè)實(shí)例包含相同的域的值就被認(rèn)為是相同,或者它們會(huì)獨(dú)立地改變。如果它們被認(rèn)為是相同的,那么這個(gè)類(lèi)型就該被定義成不可變的。
再次強(qiáng)調(diào)下, Julia 中不可變類(lèi)型有兩個(gè)重要的特性:
對(duì)于有著 C/C++ 背景的讀者, 需要仔細(xì)想下為什么這兩個(gè)特性是息息相關(guān)的。設(shè)想下,如果這兩個(gè)特性是分開(kāi)的,也就是說(shuō),如果數(shù)據(jù)在傳遞時(shí)是拷貝的, 然而數(shù)據(jù)內(nèi)部的變量可以被改變, 那么將很難界定某段代碼的實(shí)際作用。舉個(gè)例子,假設(shè) x
是某個(gè)函數(shù)的參數(shù), 同時(shí)假設(shè)函數(shù)改變了參數(shù)中的一個(gè)域:x.isprocessed = true
。根據(jù) x
是值傳遞或者引用傳遞, 在調(diào)用完函數(shù)是, 原來(lái) x
的值有可能沒(méi)有改變,
也有可能改變. 為了防止出現(xiàn)這種不確定效應(yīng), Julia 限定如果參數(shù)是值傳遞, 其內(nèi)部域的值不可改變。
以上的三種類(lèi)型是緊密相關(guān)的。它們有相同的特性:
正因有共有的特性,這些類(lèi)型內(nèi)在地表達(dá)為同一種概念的實(shí)例,DataType
,是以下類(lèi)型之一:
julia> typeof(Real)
DataType
julia> typeof(Int)
DataType
DataType
既可以抽象也可以具體。如果是具體的,它會(huì)擁有既定的大小,存儲(chǔ)安排和(可選的)名域。所以一個(gè)位類(lèi)型是一個(gè)大小非零的 DataType
,但沒(méi)有名域。一個(gè)復(fù)合類(lèi)型是一個(gè)可能擁有名域也可以為空集(大小為零)的 DataType
。
在這個(gè)系統(tǒng)里的每一個(gè)具體的值都是某個(gè) DataType
的實(shí)例,或者一個(gè)多元組。
多元組的類(lèi)型是類(lèi)型的多元組:
julia> typeof((1,"foo",2.5))
(Int64,ASCIIString,Float64)
類(lèi)型多元組可以在任何需要類(lèi)型的地方使用:
julia> (1,"foo",2.5) :: (Int64,String,Any)
(1,"foo",2.5)
julia> (1,"foo",2.5) :: (Int64,String,Float32)
ERROR: type: typeassert: expected (Int64,String,Float32), got (Int64,ASCIIString,Float64)
如果類(lèi)型多元組中有非類(lèi)型出現(xiàn),會(huì)報(bào)錯(cuò):
julia> (1,"foo",2.5) :: (Int64,String,3)
ERROR: type: typeassert: expected Type{T<:Top}, got (DataType,DataType,Int64)
注意,空多元組 ()
的類(lèi)型是其本身:
julia> typeof(())
()
多元組類(lèi)型是關(guān)于它的組成類(lèi)型是協(xié)變的,一個(gè)多元組是另一個(gè)多元組的子類(lèi)型意味著對(duì)應(yīng)的第一個(gè)多元組的各元素的類(lèi)型是第二個(gè)多元組對(duì)應(yīng)元素類(lèi)型的子類(lèi)型。比如:
julia> (Int,String) <: (Real,Any)
true
julia> (Int,String) <: (Real,Real)
false
julia> (Int,String) <: (Real,)
false
直觀地看,這就像一個(gè)函數(shù)的各個(gè)參數(shù)的類(lèi)型必須是函數(shù)簽名的子類(lèi)型(當(dāng)簽名匹配的時(shí)候)。
類(lèi)型共用體是特殊的抽象類(lèi)型,使用 Union
函數(shù)來(lái)聲明:
julia> IntOrString = Union(Int,String)
Union(String,Int64)
julia> 1 :: IntOrString
1
julia> "Hello!" :: IntOrString
"Hello!"
julia> 1.0 :: IntOrString
ERROR: type: typeassert: expected Union(String,Int64), got Float64
不含任何類(lèi)型的類(lèi)型共用體,是“底”類(lèi)型 None
:
julia> Union()
None
抽象類(lèi)型 None
是所有其它類(lèi)型的子類(lèi)型,且沒(méi)有實(shí)例。零參的 Union
調(diào)用,將返回?zé)o實(shí)例的類(lèi)型 None
。
Julia 的類(lèi)型系統(tǒng)支持參數(shù)化:類(lèi)型可以引入?yún)?shù),這樣類(lèi)型聲明為每種可能的參數(shù)組合聲明一個(gè)新類(lèi)型。
所有被聲明的類(lèi)型(DataType
的變體)都可以使用同樣的語(yǔ)法來(lái)參數(shù)化。我們將按照如下順序來(lái)討論:參數(shù)化符合類(lèi)型、參數(shù)化抽象類(lèi)型、參數(shù)化位類(lèi)型。
abstract Pointy{T}
type Point{T} <: Pointy{T}
x::T
y::T
end
類(lèi)型參數(shù)跟在類(lèi)型名后,用花括號(hào)括起來(lái):
type Point{T}
x::T
y::T
end
這個(gè)聲明定義了新參數(shù)化類(lèi)型 Point{T}
,它有兩個(gè) T
類(lèi)型的“坐標(biāo)軸”。參數(shù)化類(lèi)型可以是任何類(lèi)型(也可以是整數(shù),此例中我們用的是類(lèi)型)。具體類(lèi)型 Point{Float64}
等價(jià)于將 Point
中的 T
替換為 Float64
后的類(lèi)型。上例實(shí)際上聲明了許多種類(lèi)型:Point{Float64}
, Point{String}
, Point{Int64}
等等,因此,現(xiàn)在每個(gè)都是可以使用的具體類(lèi)型:
julia> Point{Float64}
Point{Float64} (constructor with 1 method)
julia> Point{String}
Point{String} (constructor with 1 method)
Point
本身也是個(gè)有效的類(lèi)型對(duì)象:
julia> Point
Point{T} (constructor with 1 method)
Point
在這兒是一個(gè)抽象類(lèi)型,它包含所有如 Point{Float64}
, Point{String}
之類(lèi)的具體實(shí)例:
julia> Point{Float64} <: Point
true
julia> Point{String} <: Point
true
其它類(lèi)型則不是其子類(lèi)型:
julia> Float64 <: Point
false
julia> String <: Point
false
Point
不同 T
值所聲明的具體類(lèi)型之間,不能互相作為子類(lèi)型:
julia> Point{Float64} <: Point{Int64}
false
julia> Point{Float64} <: Point{Real}
false
這一點(diǎn)非常重要:
雖然 Float64 <: Real
,但 Point{Float64} <: Point{Real}
不成立!
換句話說(shuō),Julia 的類(lèi)型參數(shù)是 不相關(guān) 的。盡管 Point{Float64}
的實(shí)例按照概念來(lái)說(shuō),應(yīng)該是 Point{Real}
的實(shí)例,但兩者在內(nèi)存中的表示上有區(qū)別:
Point{Float64}
的實(shí)例可以簡(jiǎn)便、有效地表示 64 位數(shù)對(duì)兒Point{Real}
的實(shí)例可以表示任意 Real
實(shí)例的數(shù)對(duì)兒。由于 Real
的實(shí)例可以為任意大小、任意結(jié)構(gòu),因此 Point{Real}
實(shí)際上表示指向 Real
對(duì)象的指針對(duì)兒上述區(qū)別在數(shù)組中更明顯: Array{Float64}
可以在一塊連續(xù)內(nèi)存中存儲(chǔ) 64 位浮點(diǎn)數(shù),而 Array{Real}
則保存指向每個(gè) Real
對(duì)象的指針數(shù)組。而每個(gè) Real
對(duì)象的大小,可能比 64 位浮點(diǎn)數(shù)的大。
構(gòu)造函數(shù)中將介紹如何給復(fù)合類(lèi)型自定義構(gòu)造方法,但如果沒(méi)有特殊構(gòu)造聲明時(shí),默認(rèn)有兩種構(gòu)造新復(fù)合對(duì)象的方法:一種是明確指明構(gòu)造方法的類(lèi)型參數(shù);另一種是由對(duì)象構(gòu)造方法的參數(shù)來(lái)隱含類(lèi)型參數(shù)。
指明構(gòu)造方法的類(lèi)型參數(shù):
julia> Point{Float64}(1.0,2.0)
Point{Float64}(1.0,2.0)
julia> typeof(ans)
Point{Float64} (constructor with 1 method)
參數(shù)個(gè)數(shù)應(yīng)與構(gòu)造函數(shù)相匹配:
julia> Point{Float64}(1.0)
ERROR: no method Point{Float64}(Float64)
julia> Point{Float64}(1.0,2.0,3.0)
ERROR: no method Point{Float64}(Float64, Float64, Float64)
對(duì)于帶有類(lèi)型參數(shù)的類(lèi)型,因?yàn)橹剌d構(gòu)造函數(shù)是不可能的,所以只有一種默認(rèn)構(gòu)造函數(shù)被自動(dòng)生成——這個(gè)構(gòu)造函數(shù)接受任何參數(shù)并且把們轉(zhuǎn)換成對(duì)應(yīng)的字段類(lèi)型并賦值
大多數(shù)情況下不需要提供 Point
對(duì)象的類(lèi)型,它可由參數(shù)類(lèi)型來(lái)提供信息。因此,可以不提供 T
的值:
julia> Point(1.0,2.0)
Point{Float64}(1.0,2.0)
julia> typeof(ans)
Point{Float64} (constructor with 1 method)
julia> Point(1,2)
Point{Int64}(1,2)
julia> typeof(ans)
Point{Int64} (constructor with 1 method)
上例中,Point
的兩個(gè)參數(shù)類(lèi)型相同,因此 T
可以省略。但當(dāng)參數(shù)類(lèi)型不同時(shí),會(huì)報(bào)錯(cuò):
julia> Point(1,2.5)
ERROR: `Point{T}` has no method matching Point{T}(::Int64, ::Float64)
這種情況其實(shí)也可以處理,詳見(jiàn)構(gòu)造函數(shù)。
類(lèi)似地,參數(shù)化抽象類(lèi)型聲明一個(gè)抽象類(lèi)型的集合:
abstract Pointy{T}
對(duì)每個(gè)類(lèi)型或整數(shù)值 T
,Pointy{T}
都是一個(gè)不同的抽象類(lèi)型。Pointy
的每個(gè)實(shí)例都是它的子類(lèi)型:
julia> Pointy{Int64} <: Pointy
true
julia> Pointy{1} <: Pointy
true
參數(shù)化抽象類(lèi)型也是不相關(guān)的:
julia> Pointy{Float64} <: Pointy{Real}
false
julia> Pointy{Real} <: Pointy{Float64}
false
可以如下聲明 Point{T}
是 Pointy{T}
的子類(lèi)型:
type Point{T} <: Pointy{T}
x::T
y::T
end
對(duì)每個(gè) T
,都有 Point{T}
是 Pointy{T}
的子類(lèi)型:
julia> Point{Float64} <: Pointy{Float64}
true
julia> Point{Real} <: Pointy{Real}
true
julia> Point{String} <: Pointy{String}
true
它們?nèi)匀皇遣幌嚓P(guān)的:
julia> Point{Float64} <: Pointy{Real}
false
參數(shù)化抽象類(lèi)型 Pointy
有什么用呢?假設(shè)我們要構(gòu)造一個(gè)坐標(biāo)點(diǎn)的實(shí)現(xiàn),點(diǎn)都在對(duì)角線 x = y 上,因此我們只需要一個(gè)坐標(biāo)軸:
type DiagPoint{T} <: Pointy{T}
x::T
end
Point{Float64}
和 DiagPoint{Float64}
都是 Pointy{Float64}
抽象類(lèi)型的實(shí)現(xiàn),這對(duì)其它可選類(lèi)型 T
也一樣。 Pointy
可以作為它的子類(lèi)型的公共接口。有關(guān)方法和重載,詳見(jiàn)下一節(jié) :ref:man-methods
。
有時(shí)需要對(duì) T
的范圍做限制:
abstract Pointy{T<:Real}
此時(shí), T
只能是 Real
的子類(lèi)型:
julia> Pointy{Float64}
Pointy{Float64}
julia> Pointy{Real}
Pointy{Real}
julia> Pointy{String}
ERROR: type: Pointy: in T, expected T<:Real, got Type{String}
julia> Pointy{1}
ERROR: type: Pointy: in T, expected T<:Real, got Int64
參數(shù)化復(fù)合類(lèi)型的類(lèi)型參數(shù),也可以同樣被限制:
type Point{T<:Real} <: Pointy{T}
x::T
y::T
end
下面是 Julia 的 Rational
的 immutable 類(lèi)型是如何定義的,這個(gè)類(lèi)型表示分?jǐn)?shù):
immutable Rational{T<:Integer} <: Real
num::T
den::T
end
單態(tài)類(lèi)型是一種特殊的抽象參數(shù)化類(lèi)型。對(duì)每個(gè)類(lèi)型 T
,抽象類(lèi)型“單態(tài)” Type{T}
的實(shí)例為對(duì)象 T
。來(lái)看些例子:
julia> isa(Float64, Type{Float64})
true
julia> isa(Real, Type{Float64})
false
julia> isa(Real, Type{Real})
true
julia> isa(Float64, Type{Real})
false
換句話說(shuō),僅當(dāng) A
和 B
是同一個(gè)對(duì)象,且此對(duì)象是類(lèi)型時(shí),isa(A,Type{B})
才返回真。沒(méi)有參數(shù)時(shí),Type
僅是抽象類(lèi)型,所有的類(lèi)型都是它的實(shí)例,包括單態(tài)類(lèi)型:
julia> isa(Type{Float64},Type)
true
julia> isa(Float64,Type)
true
julia> isa(Real,Type)
true
只有對(duì)象是類(lèi)型時(shí),才是 Type
的實(shí)例:
julia> isa(1,Type)
false
julia> isa("foo",Type)
false
Julia 中只有類(lèi)型對(duì)象才有單態(tài)類(lèi)型。
可以參數(shù)化地聲明位類(lèi)型。例如,Julia 中指針被定義為位類(lèi)型:
# 32-bit system:
bitstype 32 Ptr{T}
# 64-bit system:
bitstype 64 Ptr{T}
這兒的參數(shù)類(lèi)型 T
不是用來(lái)做類(lèi)型定義,而是個(gè)抽象標(biāo)簽,它定義了一組結(jié)構(gòu)相同的類(lèi)型,這些類(lèi)型僅能由類(lèi)型參數(shù)來(lái)區(qū)分。盡管 Ptr{Float64}
和 Ptr{Int64}
的表示是一樣的,它們是不同的類(lèi)型。所有的特定指針類(lèi)型,都是 Ptr
類(lèi)型的子類(lèi)型:
julia> Ptr{Float64} <: Ptr
true
julia> Ptr{Int64} <: Ptr
true
Julia 提供 typealias
機(jī)制來(lái)實(shí)現(xiàn)類(lèi)型別名。如,Uint
是 Uint32
或 Uint64
的類(lèi)型別名,這取決于系統(tǒng)的指針大小:
# 32-bit system:
julia> Uint
Uint32
# 64-bit system:
julia> Uint
Uint64
它是通過(guò) base/boot.jl
中的代碼實(shí)現(xiàn)的:
if is(Int,Int64)
typealias Uint Uint64
else
typealias Uint Uint32
end
對(duì)參數(shù)化類(lèi)型,typealias
提供了簡(jiǎn)單的參數(shù)化類(lèi)型名。Julia 的數(shù)組類(lèi)型為 Array{T,n}
,其中 T
是元素類(lèi)型, n
是數(shù)組維度的數(shù)值。為簡(jiǎn)單起見(jiàn),Array{Float64}
可以只指明元素類(lèi)型而不需指明維度:
julia> Array{Float64,1} <: Array{Float64} <: Array
true
``Vector`` 和 ``Matrix`` 對(duì)象是如下定義的:
typealias Vector{T} Array{T,1}
typealias Matrix{T} Array{T,2}
Julia 中,類(lèi)型本身也是對(duì)象,可以對(duì)其使用普通的函數(shù)。如 <:
運(yùn)算符,可以判斷左側(cè)是否是右側(cè)的子類(lèi)型。
isa
函數(shù)檢測(cè)對(duì)象是否屬于某個(gè)指定的類(lèi)型:
julia> isa(1,Int)
true
julia> isa(1,FloatingPoint)
false
typeof
函數(shù)返回參數(shù)的類(lèi)型。類(lèi)型也是對(duì)象,因此它也有類(lèi)型:
julia> typeof(Rational)
DataType
julia> typeof(Union(Real,Float64,Rational))
DataType
julia> typeof((Rational,None))
(DataType,UnionType)
類(lèi)型的類(lèi)型是什么?它們的類(lèi)型是 DataType
:
julia> typeof(DataType)
DataType
julia> typeof(UnionType)
DataType
讀者也許會(huì)注意到,DataType
類(lèi)似于空多元組(詳見(jiàn)上文 )。因此,遞歸使用 ()
和 DataType
所組成的多元組的類(lèi)型,是該類(lèi)型本身:
julia> typeof(())
()
julia> typeof(DataType)
DataType
julia> typeof(((),))
((),)
julia> typeof((DataType,))
(DataType,)
julia> typeof(((),DataType))
((),DataType)
super
可以指明一些類(lèi)型的父類(lèi)型。只有聲明的類(lèi)型(DataType
)才有父類(lèi)型:
julia> super(Float64)
FloatingPoint
julia> super(Number)
Any
julia> super(String)
Any
julia> super(Any)
Any
對(duì)其它類(lèi)型對(duì)象(或非類(lèi)型對(duì)象)使用 super
,會(huì)引發(fā) “no method” 錯(cuò)誤:
julia> super(Union(Float64,Int64))
ERROR: `super` has no method matching super(::Type{Union(Float64,Int64)})
julia> super(None)
ERROR: `super` has no method matching super(::Type{None})
julia> super((Float64,Int64))
ERROR: `super` has no method matching super(::Type{(Float64,Int64)})
更多建議: