Julia v0.6参数化复合类型构造函数与new关键字疑问
先把你给出的测试代码拆分成清晰的两段,方便对照:
第一段可正常运行的代码
workspace() println("\nSTART") struct Point{T<:Real} x::T y::T # 内部参数化构造函数 Point{G}(x,y) where {G<:Integer} = new(55,y) Point{H}(x,y) where {H<:AbstractFloat} = new(x, 11) end println(methods(Point)) # 外部构造函数 Point(x::T, y::T) where {T<:Integer} = Point{T}(x,y) Point(x::T, y::T) where {T<:AbstractFloat} = Point{T}(x,y) println(methods(Point)) p = Point(2,3) p2 = Point(2.0,3.0)
第二段报错的代码
workspace() println("") struct Point{T<:Real} x::T y::T Point(x::T, y::T) where {T<:Integer} = new(55, y) Point(x::T, y::T) where {T<:AbstractFloat} = new(x, 11) end println(methods(Point)) p = Point(2,3) p2 = Point(2.0,3.0)
接下来逐个解答你的疑问:
1. 第一段里的Point{G}(x,y) where {G<:Integer}为何不在methods(Point)里?它是构造函数吗?构造函数与方法的区别?
首先,这个函数绝对是构造函数,属于Julia里的内部参数化构造函数——也就是定义在struct块内部、专门针对特定参数化类型的构造逻辑。
它没出现在methods(Point)的输出里,是因为methods(Point)展示的是针对Point这个“裸类型”(不带参数的类型本身)的方法,而这个构造函数是绑定到Point{G}这种具体参数化类型上的。如果你想查看它,可以运行methods(Point{Int})(换成其他Integer子类型也可以),就能看到这个构造函数的身影了。
再说说构造函数和普通方法的核心区别:
- 构造函数是专门用来创建复合类型实例的特殊方法,所有构造函数最终都必须调用
new关键字来分配内存、初始化实例。内部构造函数直接和new交互,外部构造函数(比如你在struct块外定义的那两个Point(x::T,y::T))则是通过调用其他构造函数来创建实例的“包装层”。 - 普通方法是针对已经存在的实例进行操作的函数,不需要创建新实例,也没有权限调用
new。
2. new关键字的实际作用是什么?
new是Julia里的特殊函数,只能在复合类型的内部构造函数中使用,它的核心作用就两个:
- 为当前定义的复合类型分配内存空间
- 根据你传入的参数初始化结构体的字段,最后返回这个全新的实例
对于参数化类型,new可以显式指定类型参数(比如new{T}(x,y));如果省略,Julia会尝试从上下文自动推断(比如第一段代码里,因为构造函数是Point{G}(x,y),Julia能直接推断出用G作为new的类型参数)。
3. 第二段代码为何无效?
第二段代码报错的核心原因是:在Julia v0.6中,当你在带where子句的内部构造函数里使用new时,必须显式指定类型参数。
看第二段的内部构造函数:
Point(x::T, y::T) where {T<:Integer} = new(55, y)
这里的构造函数没有在函数名上标注类型参数(不像第一段的Point{G}(x,y)),Julia无法自动推断new应该使用哪个类型参数T。你需要把new改成new{T}(55, y),明确告诉Julia用当前的T来创建Point{T}实例。同理,另一个构造函数也要改成new{T}(x, 11)。
修改后的第二段代码就能正常运行了:
workspace() println("") struct Point{T<:Real} x::T y::T Point(x::T, y::T) where {T<:Integer} = new{T}(55, y) Point(x::T, y::T) where {T<:AbstractFloat} = new{T}(x, 11) end println(methods(Point)) p = Point(2,3) p2 = Point(2.0,3.0)
内容的提问来源于stack exchange,提问作者peter554

