Problems calling a variadic Java function from Clojure - java

I'm having a play with the Java NIO.2 API from JDK 7.
In particular, I want to call the method: Paths#get(String first, String... more)
This is a static method which takes in at least one string, and returns a Path object corresponding to it. There's an overloaded form: Paths#get(URI uri)
However, I can't seem to call the top method from Clojure. The nearest I can seem to get is this:
(Paths/get ^String dir-fq (object-array 0))
which fails with:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
as you might expect. After all, we're passing in an Object[] to something that's expecting String[].
I've tried removing the (object-array) form - but that just causes Clojure to try to call the get(URI) method - both with and without the type hint.
Passing nil as the second argument to Paths#get(String, String...) causes the right method to be called, but Java 7 then fails with an NPE.
I can't seem to find a way in Clojure to express the type String[] - I'm guessing I either need to do that or provide a hint to the dispatch system.
Any ideas?

As you noticed, it doesn't want an Object[], it wants a String[]. object-array does exactly what it says: it makes an array of objects. If you want to create an array with some different type, make-array and into-array are your friends. For example here:
(Paths/get "foo" (into-array String ["bar" "baz"]))
The String specifier there is optional in this case: if you leave out the array's desired type, Clojure uses the type of the first object as the array's component type.

Related

Calling the appropriate constructor for given arguments

Suppose the arguments given in the String array are:
String [] args = {"ABC","5", "4.4","true"};
Now I want to call the constructor that takes a String, integer, double and Boolean respectively by calling the method class.getConstructor(args).
I am trying to parse parameters the user passes to my program on the command line and want to configure my class accordingly.
How will I do this?
As a string like "5" can mean a String, an integer, or a double, it's impossible to go from the args array forward to the matching constructor. As #Sotirios commented, you have to look at all constructors of the class, try to parse the args elements with the appropriate functions, and if that succeeds, then the constructor is applicable.
Then calling the constructor will be the easiest part of that project.
The method getConstructor(Class<?>... parameterTypes) you mention belongs to the Reflection Framework. While reflection does have its merits and is sometimes the only way to solve a problem, you should avoid it, unless you have a good reason.
It is hard and tedious to get code right using reflection as the compiler can't detect most errors. For general use it's just a code smell.
The usual way to initialize a class is to just call the respective constructor directly, as #Ralf Kleberhoff describes: MyClass instance = new MyClass(stringParam, intParam, doubleParam, booleanParam);.
If most of the parameters are optional, you usually only pass the required parameters to the constructor. This avoids an abundance of different constructors (to be tested and documented). The constructor usually sets the optional parameters to reasonable default values. These optional parameters are then set using setters if they are available.
As you don't really describe, what you want to achieve (instead of your current problem), we can only guess...
My guess would be that you try to parse parameters the user passes to your program on the command line and want to configure your class accordingly.
My Answer to that would just be: Don't write that code! There are many good libraries out there. These are tested and offer many goodies and examples. If you insist on reinventing the wheel, these are a good starting point to see how this could be done.

Is it possible to detect number of arguments in closure and execute accordingly?

I have 3x4x5 (or with more dimensions) array and I want for each position to pass coordinates to lambda function with its value.
However I can get _D,.. 3D, 2D or 1D closure where i need to emulate (for this example) 12x5 array and 60 positions array.
Is it possible in java to get number of arguments without much overhead and invoke this that way? Can I omit reflections and do it other way or is it unavoidable?
---EDIT---
Using varargs is not an option as functions created for manipulations were defined before or we don't want to make code too complicated.
So for example when we have 3D array which normally 3D closure would access by (x,y,z,val){} then for 2D closure I need to recognise this type of lambda and use it like (x+y*X.length,z,val).
This doesn't really make sense. Every variable has a type. There is no "closure type". Rather, lambda expressions can be used in contexts that expect a functional interface type (an interface with exactly one method), and the lambda expression will evaluate to an object that implements that interface.
Since you have a known interface with exactly one method, that method has a known signature with a known number of arguments. Therefore, it doesn't make sense for you to not know the number of arguments.

Can I overload JSoup Elements class to getElementsByTag using enum elements rather than strings?

I'm writing something using JSoup, and this is something I've seen elsewhere, many of the methods take strings as arguments.
This sort of things bugs me, I'd much rather use an enum value as an argument. This'd be easy to set up using the HTML.Tag 'enum' and the .toString() method.
Then in my overloaded methods I'd translate the 'enum' to string and feed it into the original method.
So I'd go from calling:
Elements allDivs = bodyElem.getElementsByTag("DIV");
Which is prone to spelling mistakes and not knowning the right tag name, etc to calling:
Elements allDivs = bodyElem.getElementsByTag(HTML.Tag.DIV))
But I can't figure how to overload getElementsByTag to take a HTML.Tag argument.
I've tried this:
Element bodyElem = doc.body(){
Elements getElementsByTag(HTML.Tag tag){
return getElementsByTag(tag.toString());
}
};
But that doesn't work. Specifying this.getElementsByTag refers to the holding class (e.g. HTMLReader, which is one I wrote), and super.getElementsByTag refers to Object.
I'm worried that I just can't do this as Element might be final, like string in this question.
So how should I go about doing this?
JSoup's Element type is not final which means you can extend it, it looks like you are trying to extend it with an anonymous inner class, however you cannot use inner classes to extend an existing object (doc.body() returns an already instantiated Element, you can't modify it).
Your options all depend on what approach you want to take and what seems to have the best cost/complexity to benefit ratio.
1.) Extend the Element class directly and cast the result of doc.body() to your new type that contains your overloaded getElementsByTag method.
2.) Just remember to call the toString() method on your enum type every time you are passing it into the existing getElementsByTag method, it's more verbose but it's either that or cast every Element to something else.
Java eschews some tools from other languages like C#'s extension methods (which would let you do exactly what you want here) for readability and maintainability, unfortunately that sometimes leads to the edge case like this where not having them may make code a little ugly.

Does Scala have an equivalent to Haskell's Prelude.show?

Specifically I'm looking for a function that given a value will return a string that can be interpreted or compiled and give me back the same value.
For the built in types the toString() function does something similar. But you cannot use its result unmodified, e.g. it will skip the quotes in most strings (with exception to the empty string).
You could also try Scalaz shows which is similar to the Haskell show function and has similar type classes. But Scalaz is an extension library, so don't expect the same prevalence of these type classes as in Haskell.

How to cast explicitly in clojure when interfacing with java

In trying to use weka from clojure, I'm trying to convert this howto guide from the weka wiki to clojure using the java interop features of clojure.
This has worked well so far, except in one case, where the clojure reflection mechanism can't seem to find the right method to invoke - I have:
(def c-model (doto (NaiveBayes.) (.buildClassifier is-training-set)))
Later this will be invoked by the .evaluateModel method of the Evaluation class:
(.evaluateModel e-test c-model is-testing-set)
where e-test is of type weka.classifiers.Evaluation and, according to their api documentation the method takes two parameters of types Classifier and Instances
What I get from clojure though is IllegalArgumentException No matching method found: evaluateModel for class weka.classifiers.Evaluation clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:53) - I guess that this is because c-model is actually of type NaiveBayes, although it should also be a Classifier - which it is, according to instance?.
I tried casting with cast to no avail, and from what I understand this is more of a type assertion (and passes without problems, of course) than a real cast in clojure. Is there another way of explicitly telling clojure which types to cast to in java interop method calls? (Note that the original guide I linked above also uses an explicit cast from NaiveBayes to Classifier)
Full code here: /http://paste.lisp.org/display/129250
The linked javadoc contradicts your claim that there is a method taking a Classifier and an Instances - what there is, is a method taking a Classifier, an Instances, and a variable number of Objects. As discussed in a number of SO questions (the only one of which I can find at the moment is Why Is String Formatting Causing a Casting Exception?), Clojure does not provide implicit support for varargs, which are basically fictions created by the javac compiler. At the JVM level, it is simply an additional required parameter of type Object[]. If you pass a third parameter, an empty object-array, into your method, it will work fine.
IllegalArgumentException No matching method found happens anytime the arguments don't match the class. They can fail to match because no method exists with that name and number of arguments or because no method exists with that name in the called class. so also check the number and type of the arguments.
I basically always resort to repl-utils/show in these cases

Categories