I am trying to create a new SimpleLinkResolver in Clojure. This is the JavaDoc:
http://prismicio.github.io/java-kit/io/prismic/SimpleLinkResolver.html
My clojure code is:
(def lr (new io.prismic.SimpleLinkResolver))
but at the repl I get the following error:
CompilerException java.lang.InstantiationException, compiling:(form-init460449823042827832.clj:1:1)
I have no problem creating a java.util.Date:
(def d (new java.util.Date))
=> #'prismic-clojure.core/d
d
=> #inst"2018-03-17T10:30:36.016-00:00"
The above JavaDoc does say that SimpleLinkResolver is deprecated because the interface LinkResolver (http://prismicio.github.io/java-kit/io/prismic/LinkResolver.html) has default methods and so can be implemented directly. So I gave this a go to:
(def lr (new io.prismic.LinkResolver))
CompilerException java.lang.IllegalArgumentException: No matching ctor found for interface io.prismic.LinkResolver,
And I get this "no ctor" error - which I am guessing means the compiler can't find a constructor?
Questions:
Why does the first effort produce an InstantiationException?
Not being familiar with the Java-8 default methods, how would I create a new LinkResolver using its default methods?
Thanks
Why does the first effort produce an InstantiationException?
You can't instantiate an abstract class:
public abstract class SimpleLinkResolver
Not being familiar with the Java-8 default methods, how would I create a new LinkResolver using its default methods?
You'll need to implement LinkResolver interface, which can be done using Clojure's reify:
(def resolver
(reify LinkResolver
(^String resolve [this ^Fragment$DocumentLink link]
"a string"))) ;; put actual impl. here
(.resolve resolver nil)
;; => "a string"
Note you need to type-hint the return value (and arguments) because .resolve() is an overloaded method.
Also, you typically see (Class.) dot-syntax rather than (new Class) to instantiate Java classes.
Related
I'm trying to implement the following Java interface in Clojure:
package quickfix;
public interface MessageFactory {
Message create(String beginString, String msgType);
Group create(String beginString, String msgType, int correspondingFieldID);
}
The following Clojure code is my attempt at doing this:
(defn -create-message-factory
[]
(reify quickfix.MessageFactory
(create [beginString msgType]
nil)
(create [beginString msgType correspondingFieldID]
nil)))
This fails to compile with the error:
java.lang.IllegalArgumentException: Can't define method not in interfaces: create
The documentation suggests overloaded interface methods are ok, so long as the arity is different as it is in this case:
If a method is overloaded in a protocol/interface, multiple
independent method definitions must be supplied. If overloaded with
same arity in an interface you must specify complete hints to
disambiguate - a missing hint implies Object.
How can I get this working?
You're missing a parameter. The first parameter of every method implemented by reify is the object itself (as is the case with defrecord/deftype). So, try this:
(defn -create-message-factory
[]
(reify quickfix.MessageFactory
(create [this beginString msgType]
nil)
(create [this beginString msgType correspondingFieldID]
nil)))
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.
I'm writing a Clojure application, which uses BouncyCastle to encrypt data when being transmitted over the internet. However, BouncyCastle has some very strangely written classes, especially one called ECCurve (elliptic curve). It is just an abstract class extended by two classes Fp and F2m (different kinds of curves). However, these are written just as public static classes inside ECCurve (rather than being in separate files).
So the question is: when I'm creating an instance of a class declared as static inside of a different, abstract class, how do I refer to it? I've tried this:
(def curve (new ECCurve.Fp params)) ; naive, I know (ECCurve is imported btw)
(def curve (new ECCurve/Fp params))
(def curve (new (. ECCurve Fp) params))
(def curve (new (ECCurve/Fp) params))
(def curve (new org.bouncycastle.math.ec.ECCurve.Fp params))
... but neither of these work (compiler says ClassNotFoundException was thrown). Any idea, how to do this right?
Thanks,
David
ECCurve$Fp is the real classname. Javac lets you use ECCurve.Fp as basically syntactic sugar.
Apparently, you can't call apply with a record constructor:
(defrecord Foo. [id field])
(apply Foo. my-list)
fails at read time because it is not expecting Foo. in that place.
The only obvious workaround I could think of was to add a factory function:
(make-foo [id field] (Foo. id field))
which can be apply'ed of course.
Am I missing anything? I'd expect this from C#/Java but just thought it was a bit disappointing in Clojure...
Circling back on this post-1.3....
In Clojure 1.3, defrecord creates two generated constructor functions. Given:
(defrecord Person [first last])
this will create a positional constructor function ->Person:
(->Person "alex" "miller")
and a map constructor function map->Person:
(map->Person {:first "string"})
Because this is a map, all keys are optional and take on a nil value in the constructed object.
You should require/use these functions from the ns where you declare the record, but you do not need to import the record class as you would when using the Java class constructor.
More details:
http://dev.clojure.org/display/design/defrecord+improvements
http://groups.google.com/group/clojure/browse_thread/thread/ce22faf3657ca00a/beb75e61ae0d3f53
Foo. is a Java class constructor so it has typical Java interop constraints with how you call it. Creating a constructor function is a common solution (it also means you don't have to import the Foo when in a different namespace).
The problem is known and there is lots of talk about it on the Clojure mailing list. More support will probably be added in future Clojure versions.
For now you have to use your own functions or use https://github.com/david-mcneil/defrecord2 which supports some features like:
print in an eval'able form
provide clojure function as constructor
accept named parameters (maps) in constructor
participate in pre/post walk multi-method
After defining a record and the interfaces it implements, I can call its methods either by its name or using the java interop way using the dot operator.
user=> (defprotocol Eat (eat [this]))
Eat
user=> (defrecord animal [name] Eat (eat [this] "eating"))
user.animal
user=> (eat (animal. "bob"))
"eating"
user=> (.eat (animal. "bob"))
"eating"
user=>
Under the surface, what is going on there? Are there new clojure functions being defined? What happens when there are functions you defined that share the same name (is this possible?), how are these ambiguities resolved?
Also, is it possible to "import" java methods for other java objects so that you do not need the . operator so that behavior is like above? (For the purpose, for example, of unifying the user interface)
When you define a protocol, each of its methods are created as functions in your current namespaces. It follows that you can't have two protocols defining the same function in the same namespace. It also means that you can have them in separate namespaces and that a given type can extend both[1] of them without any nameclash because they are namespaced (in opposition to Java where a single class can't implement two interfaces with homonymous methods).
From a user perspective, protocol methods are no different from plain old non-polymorphic functions.
The fact that you can call a protocol method using interop is an implementation detail. The reason for that is that for each protocol, the Clojure compiler creates a corresponding backing interface. Later on when you define a new type with inline protocol extensions, then this type will implement these protocols' backing interfaces.
Consequently you can't use the interop form on an object for which the extension hasn't been provided inline:
(defrecord VacuumCleaner [brand model]
(extend-protocol Eat
VacuumCleaner
(eat [this] "eating legos and socks"))
(.eat (VaacumCleaner. "Dyson" "DC-20"))
; method not found exception
The compiler has special support for protocol functions so they are compiled as an instance check followed by a virtual method call, so when applicable (eat ...) will be as fast as (.eat ...).
To reply to "can one import java methods", you can wrap them in regular fns:
(def callme #(.callme %1 %2 %3))
(obviously you may need to add other arities to account for overloads and type hints to remove reflection)
[1] however you can't extend both inline (at least one of them must be in a extend-* form), because of an implementation limitation