Julia 中的重载与覆盖
Overloading vs. Overriding in Julia
我不熟悉 Julia,但我觉得我注意到它允许您使用不同的签名多次定义函数,例如 this:
FK5Coords{e}(ra::T, dec::T) where {e,T<:AbstractFloat} = FK5Coords{e, T}(ra, dec)
FK5Coords{e}(ra::Real, dec::Real) where {e} =
FK5Coords{e}(promote(float(ra), float(dec))...)
对我来说,这似乎允许您使用两个不同的签名调用 FK5Coords
。
所以我想知道 (a) 如果这是真的,如果 Julia 允许在函数中使用 overloading functions like this, and (b) if Julia allows something like super
,这似乎会与重载发生冲突。以及 (c),一段 Julia 代码的示例片段显示了 (1) 在一个示例中重载,以及 (2) 在另一个示例中覆盖。
我问的原因是因为我想知道 Julia 是如何解决同时具有 super
和函数重载的问题的,因为两者都需要再次定义函数,而且您似乎必须将其标记为一些元数据或要说的 "in this case I am overriding" 或 "in this case I am overloading".
注意:如果这不是重载的例子,那么(来自维基百科)这就是我想象的 Julia 支持的(沿着这些线):
// volume of a cylinder
double volume(const double r, const int h)
{
return 3.1415926*r*r*static_cast<double>(h);
}
// volume of a cuboid
long volume(const long l, const int b, const int h)
{
return l*b*h;
}
So I'm wondering (a) if that is true, if Julia allows overloading functions like this
Julia 允许您编写在参数 type/number 上不同的同一函数的不同版本(函数的 "methods" 不同)。这与重载非常相似,除了重载通常意味着要调用的函数是根据参数的编译时类型决定的,而在 Julia 中它是根据参数的 运行 时间类型决定的。这通常称为动态调度。请参阅 this C++ example 以了解重载所缺少的内容和分派给您的内容。
(b) if Julia allows something like super in a function, which seems like it would conflict with overloading
The reason I'm asking is because I am wondering how Julia solves the problem of having both super and function overloading, because both require defining the function again and it seems you would have to flag it with some metadata or something to say "in this case I am overriding" or "in this case I am overloading".
我不确定为什么您认为重载会与 super
冲突。在 C++ 中,覆盖涉及具有完全相同的参数数量和类型,而重载需要参数数量或类型不同。编译器足够聪明,可以轻松区分这两种情况,AFAICT C++ can 有一个 super
方法,尽管它同时具有重载和覆盖,除了它还具有多重继承。我相信(以我有限的 C++ 知识)多重继承是 C++ 没有 super
方法调用而不是重载的原因。
无论如何,如果您揭开面向对象的帷幕并查看方法签名,您会发现所有覆盖实际上都是一种特殊类型的重载:Dog::run(int dist, int dir)
可以覆盖 Animal::run(int dist, int dir)
(假设 Dog 继承自 Animal),但这等同于用 run(Dog d, int dist, int dir)
定义重载 run(Animal a, int dist, int dir)
函数。 (如果 run
是一个虚函数,这将是动态分配而不是重载,但那是一个单独的讨论。)
在 Julia 中,我们明确地执行此操作,因此定义为 run(d::Dog, dist::Int, dir::Int)
和 run(a::Animal, dist::Int, dir::Int)
。然而,在 Julia 中,你只能从抽象类型继承,所以这里的超类型 Animal 将是一个抽象类型,所以你不能真正用 Animal
实例调用第二个方法 - 第二个方法定义实际上是一个shorthand "call this method for any instance of some concrete subtype of Animal, unless that subtype has its own separate method definition" 的表达方式(在这种情况下,Dog 会这样做)。我不知道有什么简单的方法可以从第一个 run(Dog...
调用第二个方法 run(Animal...
,这相当于 super
调用。
(您也可以 'override' 来自另一个模块的方法 import
,但如果它具有完全相同的参数和参数类型,您可能会提交 type piracy,这通常是个坏主意。我不知道在这种类型的覆盖之后有什么方法可以恢复原始方法。"Overloading"(使用分派)通过定义和使用你自己的类型无论如何都更常见。)
(c), what an example snippet of Julia code looks like that shows (1) overloading in one example, and (2) overriding in the other.
您发布的第一个代码片段是使用分派的示例(这是 Julia 使用的,而不是重载)。再举个例子,我们先定义我们的基类型和函数:
abstract type Vehicle end
function move(v::Vehicle, dist::Float64)
println("Moving by $dist meters")
end
现在我们可以通过这种方式创建此函数的另一个调度方法("overload"):
function move(v::Vehicle, dist::LightYears)
println("Blazing across $dist light years")
end
我们也可以做一个面向对象的风格"override"(虽然在语言层面这只是被视为另一种调度方法):
struct Car <: Vehicle
model::String
end
function move(c::Car, dist::Float64)
println("$model is moving $dist meters")
end
这相当于将派生 class 中的 Vehicle.move(float dist)
覆盖为 Car.move(float dist)
。
而且只是为了它,问题中的体积函数:
# volume of a cylinder
volume(r::Float64, h::Int) = π*r*r*h
volume(l::Int, b::Int, h::Int) = l*b*h;
现在调用的正确 volume
方法将根据传递的参数的数量(和类型)来决定(return 类型由编译器自动推断,Float64
用于第一种方法,Int
用于第二种方法)。
我不熟悉 Julia,但我觉得我注意到它允许您使用不同的签名多次定义函数,例如 this:
FK5Coords{e}(ra::T, dec::T) where {e,T<:AbstractFloat} = FK5Coords{e, T}(ra, dec)
FK5Coords{e}(ra::Real, dec::Real) where {e} =
FK5Coords{e}(promote(float(ra), float(dec))...)
对我来说,这似乎允许您使用两个不同的签名调用 FK5Coords
。
所以我想知道 (a) 如果这是真的,如果 Julia 允许在函数中使用 overloading functions like this, and (b) if Julia allows something like super
,这似乎会与重载发生冲突。以及 (c),一段 Julia 代码的示例片段显示了 (1) 在一个示例中重载,以及 (2) 在另一个示例中覆盖。
我问的原因是因为我想知道 Julia 是如何解决同时具有 super
和函数重载的问题的,因为两者都需要再次定义函数,而且您似乎必须将其标记为一些元数据或要说的 "in this case I am overriding" 或 "in this case I am overloading".
注意:如果这不是重载的例子,那么(来自维基百科)这就是我想象的 Julia 支持的(沿着这些线):
// volume of a cylinder
double volume(const double r, const int h)
{
return 3.1415926*r*r*static_cast<double>(h);
}
// volume of a cuboid
long volume(const long l, const int b, const int h)
{
return l*b*h;
}
So I'm wondering (a) if that is true, if Julia allows overloading functions like this
Julia 允许您编写在参数 type/number 上不同的同一函数的不同版本(函数的 "methods" 不同)。这与重载非常相似,除了重载通常意味着要调用的函数是根据参数的编译时类型决定的,而在 Julia 中它是根据参数的 运行 时间类型决定的。这通常称为动态调度。请参阅 this C++ example 以了解重载所缺少的内容和分派给您的内容。
(b) if Julia allows something like super in a function, which seems like it would conflict with overloading The reason I'm asking is because I am wondering how Julia solves the problem of having both super and function overloading, because both require defining the function again and it seems you would have to flag it with some metadata or something to say "in this case I am overriding" or "in this case I am overloading".
我不确定为什么您认为重载会与 super
冲突。在 C++ 中,覆盖涉及具有完全相同的参数数量和类型,而重载需要参数数量或类型不同。编译器足够聪明,可以轻松区分这两种情况,AFAICT C++ can 有一个 super
方法,尽管它同时具有重载和覆盖,除了它还具有多重继承。我相信(以我有限的 C++ 知识)多重继承是 C++ 没有 super
方法调用而不是重载的原因。
无论如何,如果您揭开面向对象的帷幕并查看方法签名,您会发现所有覆盖实际上都是一种特殊类型的重载:Dog::run(int dist, int dir)
可以覆盖 Animal::run(int dist, int dir)
(假设 Dog 继承自 Animal),但这等同于用 run(Dog d, int dist, int dir)
定义重载 run(Animal a, int dist, int dir)
函数。 (如果 run
是一个虚函数,这将是动态分配而不是重载,但那是一个单独的讨论。)
在 Julia 中,我们明确地执行此操作,因此定义为 run(d::Dog, dist::Int, dir::Int)
和 run(a::Animal, dist::Int, dir::Int)
。然而,在 Julia 中,你只能从抽象类型继承,所以这里的超类型 Animal 将是一个抽象类型,所以你不能真正用 Animal
实例调用第二个方法 - 第二个方法定义实际上是一个shorthand "call this method for any instance of some concrete subtype of Animal, unless that subtype has its own separate method definition" 的表达方式(在这种情况下,Dog 会这样做)。我不知道有什么简单的方法可以从第一个 run(Dog...
调用第二个方法 run(Animal...
,这相当于 super
调用。
(您也可以 'override' 来自另一个模块的方法 import
,但如果它具有完全相同的参数和参数类型,您可能会提交 type piracy,这通常是个坏主意。我不知道在这种类型的覆盖之后有什么方法可以恢复原始方法。"Overloading"(使用分派)通过定义和使用你自己的类型无论如何都更常见。)
(c), what an example snippet of Julia code looks like that shows (1) overloading in one example, and (2) overriding in the other.
您发布的第一个代码片段是使用分派的示例(这是 Julia 使用的,而不是重载)。再举个例子,我们先定义我们的基类型和函数:
abstract type Vehicle end
function move(v::Vehicle, dist::Float64)
println("Moving by $dist meters")
end
现在我们可以通过这种方式创建此函数的另一个调度方法("overload"):
function move(v::Vehicle, dist::LightYears)
println("Blazing across $dist light years")
end
我们也可以做一个面向对象的风格"override"(虽然在语言层面这只是被视为另一种调度方法):
struct Car <: Vehicle
model::String
end
function move(c::Car, dist::Float64)
println("$model is moving $dist meters")
end
这相当于将派生 class 中的 Vehicle.move(float dist)
覆盖为 Car.move(float dist)
。
而且只是为了它,问题中的体积函数:
# volume of a cylinder
volume(r::Float64, h::Int) = π*r*r*h
volume(l::Int, b::Int, h::Int) = l*b*h;
现在调用的正确 volume
方法将根据传递的参数的数量(和类型)来决定(return 类型由编译器自动推断,Float64
用于第一种方法,Int
用于第二种方法)。