Subtyping answers a practical question: can a value be used where a particular type is required? A value’s type must be a subtype of the allowed type. An array with element type Any can hold a string, but an array with element type Integer cannot. Related types are not necessarily interchangeable: Float64 is not a subtype of Integer. section 7.2
Check a relationship
Julia’s <: operator tests subtyping: Int8 <: Integer evaluates to true, while Float64 <: Integer evaluates to false. Use subtypes(Integer) to inspect types immediately below Integer, or repeatedly call supertype to follow a type toward Any. section 7.2
The same <: operator declares a subtype. In a battle simulation, abstract type Warrior end establishes a shared supertype, and mutable struct Archer <: Warrior defines a concrete type whose objects can be created. Abstract types cannot be instantiated or have fields; concrete types can have fields but cannot be subtypes of other concrete types. Giving every warrior a name and health field is a convention, not a requirement enforced by Warrior. section 7.3.1
Listing 7.5 Definition of types in battle simulator
abstract type Warrior end #1
mutable struct Archer <: Warrior #2
name::String
health::Int
arrows::Int
end
mutable struct Pikeman <: Warrior
name::String
health::Int
end
mutable struct Knight <: Warrior
name::String
health::Int
mounted::Bool #3
end
The declarations show three concrete warrior types sharing an abstract supertype while keeping different fields. section 7.3.1
That shared supertype supports multiple dispatch. A method declared as battle!(a::Warrior, b::Warrior) accepts an Archer and a Knight because both are Warrior subtypes. Inside it, a call to attack! can select a method using the arguments’ concrete types at runtime. section 7.4.2
Subtyping also applies to union types: String and Int64 are each subtypes of Union{Int64, String}, but Float64 is not. A field typed Union{Wagon, Nothing} can therefore hold another wagon or nothing, providing a way to end a chain. section 10.2.2



