21

我在一本书中读到“你不能在 julia 中使用单一调度风格的方法创建传统的‘类’,比如obj.myfunc() ……我认为这听起来更像是一个挑战而不是事实。

因此,这是我的JavaClass类型,带有公共/私有字段和方法,只是为了在 Julia 中出现这样丑陋的东西所带来的震惊和恐怖因素,毕竟开发人员已经竭尽全力避免它:

type JavaClass

    # Public fields
    name::String

    # Public methods
    getName::Function
    setName::Function
    getX::Function
    getY::Function
    setX::Function
    setY::Function

    # Primary Constructor - "through Whom all things were made."
    function JavaClass(namearg::String, xarg::Int64, yarg::Int64)

        # Private fields - implemented as "closed" variables
        x = xarg
        y = yarg

        # Private methods used for "overloading"
        setY(yarg::Int64) = (y = yarg; return nothing)
        setY(yarg::Float64) = (y = Int64(yarg * 1000); return nothing)

        # Construct object
        this = new()
        this.name = namearg
        this.getName = () -> this.name
        this.setName = (name::String) -> (this.name = name; return nothing)
        this.getX = () -> x
        this.getY = () -> y
        this.setX = (xarg::Int64) -> (x = xarg; return nothing)
        this.setY = (yarg) -> setY(yarg) #Select appropriate overloaded method

        # Return constructed object
        return this
    end

    # a secondary (inner) constructor
    JavaClass(namearg::String) = JavaClass(namearg, 0,0)
end

示例使用:

julia> a = JavaClass("John", 10, 20);

julia> a.name # public
"John"

julia> a.name = "Jim";

julia> a.getName()
"Jim"

julia> a.setName("Jack")

julia> a.getName()
"Jack"

julia> a.x # private, cannot access
ERROR: type JavaClass has no field x

julia> a.getX()
10

julia> a.setX(11)

julia> a.getX()
11

julia> a.setY(2) # "single-dispatch" call to Int overloaded method

julia> a.getY()
2

julia> a.setY(2.0)

julia> a.getY()  # "single-dispatch" call to Float overloaded method
2000

julia> b = JavaClass("Jill"); # secondary constructor

julia> b.getX()
0

本质上,构造函数变成了一个闭包,这就是创建“私有”字段和方法/重载的方式。有什么想法吗?(除了“OMG 为什么???你为什么要这样做??”
还有其他方法吗?
你能想象到什么情况下这可能会严重失败?

4

2 回答 2

39

虽然这当然不是在 Julia 中创建对象和方法的惯用方式,但它也没有什么可怕的错误。在任何带有闭包的语言中,您都可以像这样定义自己的“对象系统”,例如查看在 Scheme 中开发的许多对象系统。

在 julia v0.5 中,有一种特别巧妙的方法可以做到这一点,因为闭包自动将它们捕获的变量表示为对象字段。例如:

julia> function Person(name, age)
        getName() = name
        getAge() = age
        getOlder() = (age+=1)
        ()->(getName;getAge;getOlder)
       end
Person (generic function with 1 method)

julia> o = Person("bob", 26)
(::#3) (generic function with 1 method)

julia> o.getName()
"bob"

julia> o.getAge()
26

julia> o.getOlder()
27

julia> o.getAge()
27

您必须返回一个函数才能执行此操作,这很奇怪,但它确实存在。这得益于许多优化,例如为您确定精确字段类型的语言,因此在某些情况下,我们甚至可以内联这些“方法调用”。另一个很酷的功能是该功能的底线控制哪些字段是“公共的”;那里列出的任何内容都将成为对象的一个​​字段。在这种情况下,您只获得方法,而不是名称和年龄变量。但是,如果您添加name到列表中,那么您也可以这样做o.name。当然方法也是多方法;您可以为 etc. 添加多个定义getOlder,它会像您期望的那样工作。

于 2016-08-25T16:34:48.900 回答
2

Jeff Bezanson 的回答非常好,但正如评论中提到的那样,字段可能会被装箱,这很烦人。

这个问题有一个更好的解决方案。

备选方案 1(与问题中提出的方法基本相同):

# Julia
mutable struct ExampleClass
    field_0
    field_1
    method_0
    method_1
    method_2

    function ExampleClass(field_0, field_1)
        this = new()

        this.field_0 = field_0
        this.field_1 = field_1

        this.method_0 = function()
            return this.field_0 * this.field_1
        end

        this.method_1 = function(n)
            return (this.field_0 + this.field_1) * n
        end

        this.method_2 = function(val_0, val_1)
            this.field_0 = val_0
            this.field_1 = val_1
        end

        return this
    end
end

ex = ExampleClass(10, 11)
ex.method_0()
ex.method_1(1)
ex.method_2(20, 22)

备选方案 2:

mutable struct ExampleClass
    field_0
    field_1

    function ExampleClass(field_0, field_1)
        this = new()

        this.field_0 = field_0
        this.field_1 = field_1

        return this
    end
end

function Base.getproperty(this::ExampleClass, s::Symbol)
    if s == :method_0
        function()
            return this.field_0 * this.field_1
        end
    elseif s == :method_1
        function(n)
            return (this.field_0 + this.field_1) * n
        end
    elseif s == :method_2
        function(val_0, val_1)
            this.field_0 = val_0
            this.field_1 = val_1
        end
    else
        getfield(this, s)
    end
end

ex = ExampleClass(10, 11)
ex.method_0()
ex.method_1(1)
ex.method_2(20, 22)

备选方案 1 看起来更好,但备选方案 2 表现更好。

我对此事进行了更深入的分析,您可以在这里查看:https ://acmion.com/blog/programming/2021-05-29-julia-oop/

于 2021-06-02T13:22:08.160 回答