What is multiple dispatch in Julia?
Updated Feb 20, 2026
Short answer
Multiple dispatch selects which method of a function to run based on the runtime types of all its arguments, not just the first. Object-oriented single dispatch picks a method from the receiver alone — a.combine(b) dispatches on a — whereas Julia's combine(a, b) considers both. It is Julia's central organising principle and the reason unrelated packages compose so readily.
Deep explanation
collide(a::Asteroid, b::Asteroid) = "both shatter"collide(a::Asteroid, b::Ship) = "ship takes damage"collide(a::Ship, b::Asteroid) = "ship takes damage"collide(a::Ship, b::Ship) = "both dock"Four methods of one function, chosen by the pair of types. In a single-dispatch language this is awkward: asteroid.collide(ship) dispatches only on the asteroid, so Asteroid must know about every type it might hit. The usual workarounds are the visitor pattern or long type checks — both existing purely to simulate the dispatch the language does not provide.
Why this makes composition work. In Julia, adding a method does not require modifying either type. A package author defining a Quaternion type can add +(a::Quaternion, b::Quaternion) to Base's +. A separate author writing a differential-equation solver calls +, and their code works with quaternions immediately — neither author knew about the other.
This is why Julia packages interoperate to an unusual degree. A measurement-uncertainty package and an ODE solver, written independently, combine to propagate uncertainty through a simulation because both are written against generic functions and dispatch resolves the rest.
Method specificity. Julia picks the most specific applicable method. Given f(x::Number) and f(x::Int), calling f(3) selects the Int one. Ambiguities — where neither of two methods is more specific — are a real error Julia reports at call time, usually resolved by defining a more specific method covering the overlap.
The relationship to performance. Dispatch is generally resolved at compile time, because the compiler already specialises on concrete argument types. So multiple dispatch is not a runtime cost in type-stable code — it is how Julia achieves both genericity and speed at once, rather than trading one for the other.
The trade-off is that behaviour is distributed across the codebase. Methods for one function may live in many packages, and answering "what happens when I call this?" requires methods() and @which rather than reading one class. It is a different mental model, not a strictly worse one — but it is unfamiliar to those coming from OO.
Real-world example
Extending a foreign type with a foreign function — neither of which you own:
using Dates
# add Base's `+` for a type from another package, in YOUR packagestruct Money amount::Float64 currency::Symbolend
Base.:+(a::Money, b::Money) = a.currency == b.currency ? Money(a.amount + b.amount, a.currency) : error("currency mismatch")
sum([Money(10.0, :GBP), Money(5.5, :GBP)]) # works: sum() calls +sum was written years earlier with no knowledge of Money, and Money needed no interface declaration or inheritance. Dispatch connects them. Doing this in a single-dispatch language requires either owning one of the two, or an adapter layer.
Common mistakes
- - Assuming dispatch works like OO overloading
- overloading is resolved at compile time on static types, whereas dispatch uses runtime types of all arguments.
- - Defining overly generic methods such as `f(x::Any, y::Any)`, which creates ambiguities with more specific methods elsewhere.
- - Type-piracy — defining methods on types and functions you do not own, which can silently change behaviour for other packages.
- - Over-constraining argument types, which blocks the generic composition that makes dispatch valuable in the first place.
- - Expecting dispatch to cost runtime performance
- in type-stable code it is resolved at compile time.
- - Forgetting that method definitions are global, so two packages defining the same signature conflict at load time.
Follow-up questions
- How does multiple dispatch differ from function overloading?
- What is type piracy and why is it discouraged?
- How does Julia resolve ambiguous methods?
- Why does multiple dispatch help package composition?