Types 朱利安的属性继承替代方案是什么?

Types 朱利安的属性继承替代方案是什么?,types,abstract-class,julia,abstract,Types,Abstract Class,Julia,Abstract,在许多编程语言中,父类可以要求任何子类包含特定字段 如果该字段是静态的,则可以通过以下方式在Julia中实现相同的效果 julia> abstract Fruit julia> type Apple <: Fruit end julia> type Orange <: Fruit end julia> type Banana <: Fruit end julia> color(::Apple) = :red color (generic f

在许多编程语言中,父类可以要求任何子类包含特定字段

如果该字段是静态的,则可以通过以下方式在Julia中实现相同的效果

julia> abstract Fruit

julia> type Apple <: Fruit end

julia> type Orange <: Fruit end

julia> type Banana <: Fruit end

julia> color(::Apple) = :red
color (generic function with 1 method)

julia> color(::Orange) = :orange
color (generic function with 2 methods)

julia> color(::Banana) = :yellow
color (generic function with 3 methods)
这些类型必须是不同的,因为它们可能还有其他属性,并且为每种类型定义的方法可能是唯一的

  • 我如何执行此要求?理想情况下,我不必在任何子类型中指定
    name
    字段,但如果必须指定,也可以
  • 如果这在朱莉娅身上是不可能的,那么建议的替代方案是什么?我可以重构这种类型的代码以完全消除这种行为的需要吗
我如何强制执行它? 通过测试

using Base.Test 
@testset "Field Contract" begin
    for sub in subtypes(Pet)
        @test fieldtype(sub, :name) == String 
        #will error if no field names string.
        #will fail if it is has wrong type
    end
end
如何在子类型上自动填写 从技术上讲,通过元编程实现这一点是可能的。 我不推荐。 这只是一个黑客概念证明:

macro declare_abstract(typename, fields...)
    quote
        abstract $(esc(typename))
        const $(esc(Symbol(:__abfields_,typename)))  = $(esc(fields))
        $(esc(typename))
    end
end

error("Please don't use this in real code") #prevent trivial copy paste

macro declare(type_expr)
    @assert type_expr.head == :type
    @assert type_expr.args[2].head == :(<:)
    parent_typename::Symbol = type_expr.args[2].args[2]
    if isdefined(Symbol(:__abfields_, parent_typename))
        @assert type_expr.args[3].head == :block
        abfields = eval(Symbol(:__abfields_, parent_typename)) #Read a globel constant. Iffy practice right here -- using eval in a macro

        push!(type_expr.args[3].args, abfields[1].args...)
    end
    type_expr
end
这种“通过测试强制契约”是一种动态语言模式

结论 依靠方法更正确。 方法定义功能。 字段只是一个实现细节。 作为抽象类型的定义者,更重要的是作为使用抽象类型的方法的定义者,您非常清楚每个子类型必须具有哪些功能。 您对实现不太确定

如果您的方法将抽象类型上的字段作为参数, 考虑它是否写的正确方式

macro declare_abstract(typename, fields...)
    quote
        abstract $(esc(typename))
        const $(esc(Symbol(:__abfields_,typename)))  = $(esc(fields))
        $(esc(typename))
    end
end

error("Please don't use this in real code") #prevent trivial copy paste

macro declare(type_expr)
    @assert type_expr.head == :type
    @assert type_expr.args[2].head == :(<:)
    parent_typename::Symbol = type_expr.args[2].args[2]
    if isdefined(Symbol(:__abfields_, parent_typename))
        @assert type_expr.args[3].head == :block
        abfields = eval(Symbol(:__abfields_, parent_typename)) #Read a globel constant. Iffy practice right here -- using eval in a macro

        push!(type_expr.args[3].args, abfields[1].args...)
    end
    type_expr
end
@declare_abstract Pet (name::String), (owner_id::Int)
@declare type Cat <: Pet
end
?Cat
...

Summary:
    type Cat <: Pet
Fields:
    name     :: String
    owner_id :: Int64
using Base.Test 
@testset "Method Contract" begin
    for sub in subtypes(Pet)
        @test method_exists(get_identifier, (sub,))
    end
end