多重派发 #

一、派发概念 #

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))

六、总结 #

本章我们学习了:

  1. 派发概念:单派发 vs 多重派发
  2. 方法选择:根据所有参数类型选择
  3. 方法优先级:更具体的方法优先
  4. 实际应用:运算、渲染、序列化
  5. 方法歧义:识别和解决歧义

接下来让我们学习Julia的方法重载!

最后更新:2026-03-27