Types 朱利安的属性继承替代方案是什么?
在许多编程语言中,父类可以要求任何子类包含特定字段 如果该字段是静态的,则可以通过以下方式在Julia中实现相同的效果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> 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