clojure: Can't access non-public fields in the same package - clojure

In my clojure program I cannot access package scoped fields of the java class com.foo.Foo although I am in the namespace "com.foo" (via "(ns com.foo)" at the top of my clojure program). However, public fields in com.foo.Foo are accessible.
Why?

Two problems here:
First, the namespace com.foo is compiled to a class foo_whatever in package com; it's not compiled to a class in package com.foo.
See:
user> (ns com.foo)
nil
com.foo>
nil
com.foo> (defn hello[] "hello !")
#'com.foo/hello
com.foo> (class hello)
com.foo$hello
com.foo> (ns com.foo.hello)
nil
com.foo.hello> (defn hi[] "hi !")
#'com.foo.hello/hi
com.foo.hello> (class hi)
com.foo.hello$hi
Second, when looking for constructors or fields, the Clojure compiler uses methods getConstructor and getFields from java.lang.Class, which, by spec, only return the public constructors and public fields.
So, bad luck here. It seems you won't be able to access package-protected fields.
Edit, answering comments. The best approach for accessing package-level fields in legacy Java code would be to write a class in Java that wraps the existing class and which exposes the package protected methods and fields from that class with public methods. This way, you have more control over the name and package of the generated class.
This wrapper is a small amount of Java code, and from there you can access the fields from Clojure code.

Related

Is it possible to gen-class with a private final field?

How to create a Java class with a private final field in Clojure?
ClojureDocs for gen-class say that state field will be public
:state name
If supplied, a public final instance field with the given name will be
created.
So, in other words, do we have a way to create a class and after this a java object with the encapsulated state?
#alexmiller answered on this question recently here
In short, no. As you mention in the docs, gen-class state fields will
be public final fields. However, that field can be (for example), an
atom that is statefully modified by the implementation methods. In
general, we do not give much weight to encapsulation in Clojure -
instead preferring to make things visible, but "safe" (via
immutability). To quote Rich from
https://clojure.org/reference/datatypes, "encapsulation is folly".
gen-class is not a general purpose DSL for generating all possible
Java classes. It is a tool to generate classes in a certain style in
line with Clojure's aesthetics.
Another path however to something along these lines is to use a
deftype, which can have private mutable fields, exposed by
implementing interfaces or protocols inline. The deftype fields can
have meta of either ^:volatile-mutable or ^:unsynchronized-mutable,
both of which will become private fields.

Creating a library of Protocols and defrecords for use from Java

At the moment, I have a completely functional Clojure library which is called from Java.
The way I do this : I have a file that uses gen-class to wrap the entire API as static methods of a single class and passes data in and out in the form of IPersistentVector and IPersistentMap.
Now, however, I'm refactoring the library and putting the functionality behind various Protocols.
I have four protocols, lets call them A, B, C and D. And two defrecords, X and Y. X and Y both implement protocols A, B and C. While Y also implements D.
What do I need to do to make these available to Java? Are these automatically available as Interfaces and Classes? Or do I still have to do the equivalent of the gen-class to make them public?
If not, what is the equivalent of the gen-class :methods clause, where I define the Java types for the arguments to the methods?
Does anyone have a simple example of making Protocols and records available to Java?
defprotocol
Every Clojure protocol is also a Java interface with the same name and methods. If I take an example from ibm developerworks, we see that :
(ns com.amalgamated)
(defprotocol Fulfillment
(invoice [this] "Returns an invoice")
(manifest [this] "Returns a shipping manifest"))
Is equivalent to :
package com.amalgamated;
public interface Fulfillment {
public Object invoice();
public Object manifest();
}
Clojure.org also has some (rather terse) information on this.
A Java client looking to participate in the protocol can do so most
efficiently by implementing the protocol-generated interface. External
implementations of the protocol (which are needed when you want a
class or type not in your control to participate in the protocol) can
be provided using the extend construct:
(extend AType AProtocol
{:foo an-existing-fn
:bar (fn [a b] ...)
:baz (fn ([a]...) ([a b] ...)...)} BProtocol
{...} ...)
definterface
If you are aiming at performance, you could consider using definterface, which use is similar to the protocols. This SO post also has details about how to use it :
(definterface Foo
[^int foo [x ^String y]]
[^void bar [^ints is]])
definterface seem to be faster than protocols.
defrecord
Similarly, records (as well as deftype and definterface) will generate Java Classes.
Again, Clojure.org/datatypes has useful information (emphasis mine) :
deftype and defrecord dynamically generate compiled bytecode for a
named class with a set of given fields, and, optionally, methods for
one or more protocols and/or interfaces. They are suitable for dynamic
and interactive development, need not be AOT compiled, and can be
re-evaluated in the course of a single session. They are similar to
defstruct in generating data structures with named fields, but differ
from defstruct in that: [...]
So yes if will be available from Java.
Just be careful with naming.
As a side note, you may want to have a look at calling Clojure from Java.

can clojure definterface extend another interface?

I want to translate the following code into clojure:
package com.example.orbit.hello;
import com.ea.orbit.actors.IActor;
import com.ea.orbit.concurrent.Task;
public interface IHello extends IActor
{
Task<String> sayHello(String greeting);
}
how is this possible?
You could simply define this interface Java and use it from Clojure.
If you really want to do it in Clojure, though, that is possible as well, although you'll have to use gen-interface rather than definterface:
(gen-interface
:name fully.qualified.IName
:extends [whatever.it.INeedsTo]
:methods
[[methodName
[fully.qualified.type.of.Argument1 …] ; NB. no `this' formal argument
fully.qualified.ReturnType]])
For a real-world example, have a look at this interface definition in data.avl or this one in ctries.clj (the latter uses :extends).
The <String> part of Task<String> will not appear in the Clojure version – generics are a javac construct and do not exist at runtime on the JVM.

How to add annotations to the reified class in Clojure?

I need to create a class which implements an interface and also has an annotation, and later pass an instance of this class to some API implemented in Java.
With Clojure, I could reify the interface and get an instance like this:
(reify MyInterface (method1 [this] ...))
However, reify doesn't seem to accept annotations. How may I work around this?
First of all reify returns an object not a class. If you need to generate a class with annotations, you will need to use gen-class as shown below (the example shows adding a Deprecated annotation to the class):
(gen-class :name ^{java.lang.Deprecated true} MyClass
...)
Also, you can use deftype:
(deftype ^{java.lang.Deprecated true} MyClass
...)

how to use the bean function in clojure

I don't really understand how the bean function works when I am using it on beans. This code here throws an exception:
(import java.lang.management.ManagementFactory)
(def runtime (bean (ManagementFactory/getRuntimeMXBean))
(:name runtime)
;; =>
Class clojure.core$bean$fn__5177$fn__5178 can not access a member of class sun.management.RuntimeImpl with modifiers "public"
[Thrown class java.lang.IllegalAccessException]
but the class does have a method called getName(). http://docs.oracle.com/javase/6/docs/api/java/lang/management/RuntimeMXBean.html
You must have missed it, it's right there.
getName() Returns the name representing the runn
(import java.lang.management.ManagementFactory)
(def runtime (bean (ManagementFactory/getRuntimeMXBean))
(:name runtime)
;; =>
Class clojure.core$bean$fn_5177$fn_5178 can not access a member of class sun.management.RuntimeImpl with modifiers "public"
[Thrown class java.lang.IllegalAccessException]
but the class does have a method called getName(). http://docs.oracle.com/javase/6/docs/api/java/lang/management/RuntimeMXBean.html
ing Java virtual machine.
Edit1:
This issue is very similar to one here and response to it is here.
Class RuntimeImpl returned by ManagementFactory.getRuntimeMXBean is only package visible but it implements public visible interface RuntimeMXBean. So what happens is that function bean tries to call a method on the class RuntimImpl but it can't as class is only package visible. I think if it tried to call getMethod on interface RuntimeMXBean it would have worked.
Yeap this works:
(def mx-bean (ManagementFactory/getRuntimeMXBean))
(def interface-method
(.getMethod RuntimeMXBean "getName" (into-array java.lang.Class [])))
(.invoke interface-method mx-bean (into-array []))
I'm not sure if it's a bug or feature. I would recommend asking on Clojure mailing list.
I think this is captured by a bug right here: http://dev.clojure.org/jira/browse/CLJ-978?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
Upvote it!
You can roll-your-own bean function using the available patch.