Julia的类型
传统编程语言都可以分为静态语言或者动态语言,Julia的类型以动态类型为基本,同时支持静态类型声明以加快程序运行速度。
值得一提的是,Julia支持泛型!
在我看来,Julia的面向对象并不像Java、Python那么纯粹,更有种像Go一样的,使用一些看似面向过程的东西来实现面向对象。
Julia虽然有提供声明类型的struct、mutable struct关键词,但是这样定义类所能提供的封装性十分有限。我们有时还要在类定义结构外,去定义这个类实现的一些接口。
Type Declarations
算符::可以用来进行类型声明或者类型断言:
julia> (1+2)::AbstractFloat
ERROR: TypeError: in typeassert, expected AbstractFloat, got a value of type Int64
julia> (1+2)::Int
3
如果一个变量被该种方式声明,那么它将一直保持着这种类型(不允许进行类型修改)。Julia暂时(1.5+)并不支持在全局作用域中声明一个变量的类型,我们可以在函数中这样做,它可以提高程序运行的速度。记住,函数在编译的时候,一个变量不能进行两次类型声明!
类型声明也可以作用在函数返回中:
function sinc(x)::Float64
if x == 0
return 1
end
return sin(pi*x)/(pi*x)
end
Abstract Types
Python和Java中都有抽象类型的存在,抽象类型不能被实例化,可以用来继承,以此表现各个类的关系。Julia的抽象类类似。
在Julia中使用abstract type来声明一个抽象类:
abstract type «name» end
abstract type «name» <: «supertype» end
算符<:表示继承关系。
Julia语言本身有一套预先定义的抽象类型系统,比如,关于数:
abstract type Number end
abstract type Real <: Number end
abstract type AbstractFloat <: Real end
abstract type Integer <: Real end
abstract type Signed <: Integer end
abstract type Unsigned <: Integer end
诸如Int8、Float32等具体的类,都是继承了某个抽象类的。
Julia的任何类型,都是继承了Any这个基类的(就像Python的object)
<:也可以用来检验类型之间的继承关系,>:符号起相反的作用:
julia> Integer <: Number
true
julia> Integer <: AbstractFloat
false
julia> Integer >: Number
false
Primitive Types
Python、Java等语言的原始类型和引用类型的概念,依然适用于Julia。一个原始类型,使用primitive type来声明,一般不建议用户自定义原始类型。
primitive type «name» «bits» end
primitive type «name» <: «supertype» «bits» end
以下列出Julia内置的原始类型:
primitive type Bool <: Integer 8 end
primitive type Float64 <: AbstractFloat 64 end
primitive type Int32 <: Signed 32 end
Composite Types
Julia把“引用类型”称作是复合类型,这种类型在Julia中具体体现为一个“结构体”:
struct Foo
bar
baz::Int # 类型限制
qux::Float64
end
foo = Foo("Hello, world.", 23, 1.5)
fieldnames(foo) # (:bar, :baz, :qux)
可以用.加属性名,访问属性值。fieldnames函数返回一个实例的所有属性。
使用struct关键字声明的类型,其实例是不可变的:它们不能在被定义之后修改!
Mutable Composite Types
要让一个复合类型的实例能够改变,就要用mutable struct关键字。
julia> mutable struct Bar
baz
qux::Float64
end
julia> bar = Bar("Hello", 1.5);
julia> bar.qux = 2.0
2.0
julia> bar.baz = 1//2
1//2
可变类型的实例,是存储在堆上的。
Type Unions
Julia的一个重要特点是支持类型组合,这是类型系统为多重派发这个Julia最重要特性的支持。
julia> IntOrString = Union{Int,AbstractString}
Union{Int64, AbstractString}
julia> 1 :: IntOrString
1
julia> "Hello!" :: IntOrString
"Hello!"
julia> 1.0 :: IntOrString
ERROR: TypeError: in typeassert, expected Union{Int64, AbstractString}, got a value of type Float64
Parametric Types
Julia尽管被更多地认为是动态语言,但神奇的是,它的类型系统支持泛型。
泛型类可以用如下方式来定义:
struct Point{T}
x::T
y::T
end
于是我们可以带类型的实例化:
Point{Float64}(1.0, 2.0)
或者可以隐式地实例一个泛型类,让解释器自行推断:
Point(1.0,2.0) # Point{Float64}(1.0, 2.0)
如果不能正确推断,则会报错。这里推断的类型,都会是具体类型。
注意
Point{Float64} <: Point{Real}返回false,在多重派发中,我们使用Point{<:Real}来把Point{Float64}类包含进去。
抽象类型也可以用泛型的方式来声明:
abstract type Pointy{T} end
类似于Java,Julia支持类型界限,如下代码是Julia中有理数类型的定义方式:
struct Rational{T<:Integer} <: Real
num::T
den::T
end