如何在Clojure中使用defrecord来扩展Java类?

huangapple go评论65阅读模式
英文:

How to use defrecord in Clojure to extend a Java class?

问题

根据我的研究,扩展Java类的方式基本上可以通过命名空间中的gen-class或使用代理(proxy)来实现。但是从Clojure类型选择流程图来看,它似乎表明我可以使用record来扩展Java类:

  1. 类型是否需要扩展Java类或实现任何接口?是
  2. 您是否需要一个命名类型,还是只需要一个匿名类型的实例?命名类型
  3. 您是否需要能够在Java中静态地引用该类?否
  4. 您的类是否对领域值建模 - 因此受益于类似于哈希映射的功能和语义?是

使用defrecord

那么问题是...如何呢?

例如(来自this):

public final class Second extends Struct {
    public final Signed32 a_number = new Signed32();
    public Second(final Runtime runtime) {
        super(runtime);
    }
}

public final class Top extends Struct {              
    public final Second second = inner(new Second(getRuntime()));  
    public final Second[] seconds = array(new Second[5]);
    public final Signed32 another_number = new Signed32();
    public final Signed32[] more_numbers = array(new Signed32[5]);
    public Top(final Runtime runtime) {
        super(runtime);
    }
}

我应该...

(defrecord Second)
(extend jnr.ffi.Struct
        Second)
英文:

From my research, the way to extend a Java class is basically with either gen-class in a namespace or with proxy. But looking at the Clojure type selection flowchart, it seems to suggest that I can use record to extend a Java class:

  1. Will the type need to extend a Java class or implement any interfaces? Yes
  2. Do you need a named type or only an instance of an anonymous type? Named type
  3. Do you need to be able to refer to the class statically from Java? No
  4. Is your class modelling a domain value - thus benefiting from hasmap-like functionality and sematics? Yes

Use defrecord

So the question is... how?

For example (from this):

public final class Second extends Struct {
    public final Signed32 a_number = new Signed32();
    public Second(final Runtime runtime) {
        super(runtime);
    }
}

public final class Top extends Struct {              
    public final Second second = inner(new Second(getRuntime()));  
    public final Second[] seconds = array(new Second[5]);
    public final Signed32 another_number = new Signed32();
    public final Signed32[] more_numbers = array(new Signed32[5]);
    public Top(final Runtime runtime) {
        super(runtime);
    }
}

Do I...

(defrecord Second)
(extend jnr.ffi.Struct
        Second)

?

答案1

得分: 1

我认为流程图是错误的。查看Clojure - 数据类型:deftype、defrecord 和 reify 文档,我们可以看到:

> - deftype/defrecord 可以实现一个或多个协议和/或接口

没有提到扩展现有类。实际上,在 defrecord 本身的文档中,除了将 Object 作为特殊情况之外,没有提到扩展现有类。

抱歉,看起来 proxy 或者 gen-class 是您唯一的选择。

英文:

I think the flow chart is wrong. Checking the Clojure - Datatypes: deftype, defrecord and reify documentation we can see that:

> - a deftype/defrecord can implement one or more protocols and/or interfaces

There is no mention of extending an existing class. Indeed, looking at the documentation for defrecord itself there is no mention of extending existing classes, except for Object as a special case.

Sorry, it looks like either proxy or gen-class are your only options.

答案2

得分: 0

为什么需要命名类型?如果按照你所说,你不需要在Java中静态地引用它,我看不出有任何特定的理由来命名这个类型。如果你正确地回答了这个问题,它会引导你到proxy。你的问题在于你选择了不一致的答案:你说你需要一个命名类,但是对于你可能需要的任何理由都没有回答是。流程图所考虑的原因是Java代码可能希望通过名称引用你的代码,这时就会用到gen-class。但是一方面说你必须扩展一个Java类,另一方面说你的新类型将是一个简单的数据载体,用于建模领域值,这样说起来并没有太多意义。Clojure的记录在这方面表现得很好,但是从Java继承一个可变的讨厌基类并不能构建一个很好的领域对象。

也就是说,如果你对“建模领域类型”回答“否”,那么它会引导你到deftype,但这种情况下也不能扩展一个类。我认为,如果你想扩展一个类时它仍然允许你离开“互操作区域”,这是一个错误:最好将接口和子类化分开,但是流程图只能做到这么大。

顺便说一下,原始流程图文章的评论包括了你的问题,作者回答了这个问题,并且也提到了我建议使用proxy的问题,以及流程图已经足够大的问题:

> 第一个决策不仅仅表示你需要扩展一个类 — 它还包括实现接口。所以,在这方面有一些模糊。当然,你在gen-class的领域,但是为了消除模糊性,将事情拆分得更清楚会使流程图变得更大,我想。
>
> 值得一提的是,我个人会放弃在那里提出的命名类型的要求,使用proxy,然后只是定义(class (create-proxy-instance …)) 的结果用于实例检查等(假设这就是你需要命名类型的原因)。

英文:

Why do you need a named type? If, as you say, you don't need to refer to it statically from Java, I don't see any particular reason to name the type. And if you answer that question correctly, it leads you to proxy. Your problem is you've chosen inconsistent answers: you say you need a named class, but don't answer yes to any of the reasons you might need one. The reason the flowchart has in mind is that Java code may wish to refer to your code by name, in which case gen-class. But it makes little sense to say on the one hand that you must extend a Java class, and on the other that your new type will be a simple data carrier, modeling a domain value. Clojure records are great at that, but having a yucky mutable base class from Java doesn't make for a good domain object.

That said, if you'd answered "no" to "modeling a domain type", it would lead you to deftype, which also cannot extend a class. I think it's a mistake that it ever allows you to escape the "interop zone" if you want to extend a class: it would be better to keep interfaces and subclassing separate, but a flowchart can only be so large.

FWIW the comments on the original flowchart post include your question, with an answer by the author, echoing my suggestion to use proxy and the issue that the flowchart is big enough already:

> That first decision isn’t exclusively indicating that you need to extend a class — it includes implementing interfaces. So, there is a bit of ambiguity there. Of course, you’re in gen-class-land there, but breaking things out more to eliminate the ambiguity would make the flowchart a fair bit bigger, I think.
>
> FWIW, I’d personally drop the named type requirement there, use proxy, and just def the result of (class (create-proxy-instance …)) for instance? checks and such (assuming that that’s why you need the named type).

huangapple
  • 本文由 发表于 2020年9月2日 12:46:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/63698850.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定