Julia: Types

1,743 阅读3分钟

Julia的类型

传统编程语言都可以分为静态语言或者动态语言,Julia的类型以动态类型为基本,同时支持静态类型声明以加快程序运行速度。

值得一提的是,Julia支持泛型!

在我看来,Julia的面向对象并不像Java、Python那么纯粹,更有种像Go一样的,使用一些看似面向过程的东西来实现面向对象。

Julia虽然有提供声明类型的structmutable 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

诸如Int8Float32等具体的类,都是继承了某个抽象类的。

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

UnionAll Types