多重派发 #
一、派发概念 #
1.1 单派发 vs 多重派发 #
单派发(如Java、C++):方法根据第一个参数(this)的类型选择。
多重派发(Julia):方法根据所有参数的类型选择。
1.2 多重派发示例 #
julia
struct Cat end
struct Dog end
speak(::Cat) = "Meow!"
speak(::Dog) = "Woof!"
speak(::Cat, ::Dog) = "Cat hisses at Dog!"
speak(::Dog, ::Cat) = "Dog barks at Cat!"
speak(Cat())
speak(Dog())
speak(Cat(), Dog())
speak(Dog(), Cat())
二、多重派发机制 #
2.1 方法选择 #
Julia根据所有参数类型选择最具体的方法:
julia
function collide(a, b)
"Generic collision"
end
function collide(a::Int, b)
"Int collides with something"
end
function collide(a, b::Int)
"Something collides with Int"
end
function collide(a::Int, b::Int)
"Int collides with Int"
end
collide(1, "hello")
collide("hello", 1)
collide(1, 2)
collide("hello", "world")
2.2 方法优先级 #
更具体的方法优先:
julia
abstract type Animal end
struct Cat <: Animal end
struct Dog <: Animal end
function meet(a::Animal, b::Animal)
"Animals meet"
end
function meet(a::Cat, b::Animal)
"Cat meets animal"
end
function meet(a::Animal, b::Dog)
"Animal meets dog"
end
function meet(a::Cat, b::Dog)
"Cat meets dog"
end
meet(Cat(), Dog())
meet(Cat(), Cat())
meet(Dog(), Dog())
三、实际应用 #
3.1 数值运算 #
julia
function multiply(a::Int, b::Int)
a * b
end
function multiply(a::Float64, b::Float64)
a * b
end
function multiply(a::Int, b::Float64)
a * b
end
function multiply(a::Float64, b::Int)
a * b
end
multiply(2, 3)
multiply(2.0, 3.0)
multiply(2, 3.0)
multiply(2.0, 3)
3.2 图形渲染 #
julia
abstract type Shape end
struct Circle <: Shape
radius::Float64
end
struct Rectangle <: Shape
width::Float64
height::Float64
end
function draw(shape::Shape)
error("Cannot draw generic shape")
end
function draw(c::Circle)
println("Drawing circle with radius $(c.radius)")
end
function draw(r::Rectangle)
println("Drawing rectangle $(r.width)×$(r.height)")
end
function draw(s1::Shape, s2::Shape)
println("Drawing two shapes")
draw(s1)
draw(s2)
end
draw(Circle(5.0))
draw(Rectangle(3.0, 4.0))
draw(Circle(2.0), Rectangle(3.0, 4.0))
3.3 序列化 #
julia
function serialize(x::Int)
"int:$x"
end
function serialize(x::Float64)
"float:$x"
end
function serialize(x::String)
"string:$x"
end
function serialize(arr::Vector)
"array:[" * join(serialize.(arr), ",") * "]"
end
serialize(42)
serialize(3.14)
serialize("hello")
serialize([1, 2, 3])
四、方法歧义 #
4.1 歧义情况 #
julia
function f(x::Int, y)
println("Method 1")
end
function f(x, y::Int)
println("Method 2")
end
f(1, 1)
4.2 解决歧义 #
添加更具体的方法:
julia
function f(x::Int, y::Int)
println("Method 3")
end
f(1, 1)
五、实践练习 #
5.1 练习1:碰撞检测 #
julia
abstract type GameObject end
struct Player <: GameObject
name::String
end
struct Enemy <: GameObject
name::String
end
struct Wall <: GameObject end
function interact(a::GameObject, b::GameObject)
"Generic interaction"
end
function interact(p::Player, e::Enemy)
"$(p.name) fights $(e.name)"
end
function interact(e::Enemy, p::Player)
"$(e.name) attacks $(p.name)"
end
function interact(::Player, ::Wall)
"Player cannot pass through wall"
end
function interact(::Enemy, ::Wall)
"Enemy blocked by wall"
end
interact(Player("Hero"), Enemy("Goblin"))
interact(Enemy("Goblin"), Player("Hero"))
interact(Player("Hero"), Wall())
5.2 练习2:数学运算 #
julia
function add(a::Number, b::Number)
a + b
end
function add(a::Vector, b::Vector)
a .+ b
end
function add(a::Matrix, b::Matrix)
a + b
end
function add(a::String, b::String)
a * b
end
add(1, 2)
add([1, 2], [3, 4])
add([1 2; 3 4], [5 6; 7 8])
add("Hello, ", "Julia!")
5.3 练习3:格式化输出 #
julia
function format(x)
string(x)
end
function format(x::Float64)
@sprintf("%.2f", x)
end
function format(x::Int)
string(x)
end
function format(arr::Vector)
"[" * join(format.(arr), ", ") * "]"
end
function format(d::Dict)
"{" * join(["$k => $(format(v))" for (k, v) in d], ", ") * "}"
end
using Printf
format(3.14159)
format([1, 2.5, 3])
format(Dict("a" => 1, "b" => 2.5))
六、总结 #
本章我们学习了:
- 派发概念:单派发 vs 多重派发
- 方法选择:根据所有参数类型选择
- 方法优先级:更具体的方法优先
- 实际应用:运算、渲染、序列化
- 方法歧义:识别和解决歧义
接下来让我们学习Julia的方法重载!
最后更新:2026-03-27