Is a groovy String also implicitly a Collection? - java

Weak typing in groovy is great and all, but this one has me scratching my head:
myList = new java.util.ArrayList(['foo', 'foo', 'foobar', 'barfoo'])
myList.removeAll('foo')
println myList // prints [foobar, barfoo]
removeAll needs a Collection as a parameter. Docs. Why does line #2 work when I’m sending in a String - Is a groovy String also implicitly a Collection ?

It is rather Groovy provides enhancements to JDK Collection interface one of which is Collection::removeAll(Object[] data) and this allows such behaviour: one element foo seems to be implicitly converted into one-item array as with varargs.

No. Groovy adds an extension method Collection.removeAll(Object[]) that can accept varargs. Your 'foo' gets turned into a single-element array.
In the future, place a breakpoint on methods like this and Step Into; you'll see exactly what value was passed in and whether you are stepping into an extension method or something else unexpected.

Related

Eclipse does not suggest methods in Lambda expression

I have an ArrayList of Strings, and am adding a method to sort the ArrayList
list.sort(Comparator.comparing(x -> x.length()));
When I write x and press ctrl + space eclipse does not suggest the methods of the String class, but only shows methods of the Object class.
Please help me configure eclipse to show the exact method suggestions in this case.
In regular cases eclipse is exact.
This is a two-fold issue, one with eclipse, and one with java semantics.
Java Semantics
A quick example:
public static void main(String[] args) {
List<String> myList = new ArrayList<>();
myList.sort(Comparator.comparing(x -> x.|));
}
Assume you press ctrl + space at the | (cursor) position. Then eclipse has to infer a lot of information to know, that x is in fact an element of type String. First, the list's generic type String must be known (it is, eclipse can deduce this). Then the Comparator.comparing method needs to know, that it must return an instance of a Comparator which compares Strings, which eclipse could deduce, but here is the first issue: The Comparator could be one that compares not just Strings, but also any other kind of Object. What if you wanted to pass a method to myList.sort that is more general than the generic Comparator<String>? To be more precise: The List.sort method can take (in your case) any Comparator of type Comparator<? super String>. And ? super String is already either Object or String.
So in your example. the type of x could just be an object, eclipse cannot ultimately decide. However, you can write your lambda expression differently, to make it clear:
myList.sort(Comparator.comparing((String x) -> x.|));
In this case, the completion suggestion could be more helpful (depending on the version of eclipse).
eclipse AST issues with incomplete lambdas
An incomplete lambda expression is more often than not such an upset in the syntax of the entire file, that eclipse cannot determine the syntax tree at that position correctly. That means, that eclipse cannot deduce, that the code you are writing is supposed to be a lambda expression, where x is the parameter of the lambda function, and you want to complete that. This issue could be addressed, if the tokenizer and AST-parser of eclipse are adapted accordingly (which might have already been tried). Whether this is possible at all, I cannot answer. I only know it helps, to write a "full" lambda, with a method block, and convert that to a "slim" lambda later on:
myList.sort(Comparator.comparing((String x) -> { return x.| }));
For the above case, the completion should work (IF you specify String as absolute type of the Comparator, as I have done in the example).
Issues like this stem from the question of how to interpret the characters and therefore deduce, what the programmer might intent to write (the process of auto completion and completion suggestion).
eclipse is very strong in isolating a reference to a named entity, when in regular code, like a method block, a for loop, or any other construct. That is why it works well there. The syntax tree is usually easy to process then.
However when using lambdas, eclipse (and any other IDE for that matter) have a harder time. This is due to the fact, that lambdas work by inferring a lot of implicit information, which would otherwise need to be written explicitly (for example in an explicit implementation of the interface).
If everything else fails, you can create the explicit interface at that position and then convert to a lambda after completing it.

List forEach with method reference explanation

I have been learning java for past few months and just started to get into lambda functions. I recently switched my IDE and noticed a warning saying "Can be replaced with method reference" on codes like this.
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.forEach(num -> doSomething(num));
voiddoSomething(int num) {
System.out.println("Number is: " + num);
}
After some digging, I realized that instead of the line
intList.forEach(num -> doSomething(num));
I can just use
intList.forEach(this::doSomething);
This is just amazing. A few days ago I did not even knew about lambdas and was using for loops to do operations like this. Now I replaced my for loops with lambdas and even better, I can replace my lambdas with method references. The problem is that I don't really understand how all this works internally. Can anyone please explain or provide a good resource explaining how the doSomething function is called and the argument is passed to it when we use method reference?
The double-colon operator is simply a convenience operator for doing the same thing that your lambda is doing. Check out this page for more details: https://javapapers.com/core-java/java-method-reference/
The double colon is simply syntactic sugar for defining a lambda expression whose parameters and return type are the same as an existing function. It was created to to allow lambdas to more easily be added with existing codebases.
Calling the forEach method of a List<Integer> object takes as its parameter any object implementing the Consumer functional interface. Your lambda num -> doSomething(num) itself happens to fulfill the formal requirements of this interface.
Thus, you can use the double colon as syntactic sugar for that lambda expression.
In general, if you have an object obj with method func, which accepts parameters params... then writing obj::func is equivalent to the lambda (params...) -> obj.func(params...).
In your case, o is this (the current object), which has a method doSomething(), which takes an integer parameter, thus, this::doSomething is equivalent to num -> doSomething(num).
Given you've mentioned that it's only until recently you started getting into functional programming I'd like to keep things as simple and straightforward as possible, but note that with just the little code you've provided, we could derive a lot both from the high-level view of things as well the low-level view.
Can anyone please explain or provide a good resource explaining how
the doSomething function is called and the argument is passed to it
when we use method reference?
how the doSomething function is called is left to the library (internal iteration) regardless of whether we use a method reference or a lambda expression, so essentially we specify the what not the how meaning we provide to the forEach method a behaviour (a function) that we want to execute for each element of the source intList and not necessarily how it should go about its work.
This is then left to the library to apply (execute) the specified function of doSomething for each element of the source intList.
Method references can be seen as a shorthand for lambdas calling only a specific method. The benefit here is that by referring to a specific method name explicitly, your code gains better readability, therefore, making it easier to read and follow and in most cases reading code with method references reads as the problem statement which is a good thing.
It's also important to know that not any given function can be passed to the forEach terminal operation as every method that accepts a behaviour has a restriction on the type of function allowed. This is accomplished with the use of functional interfaces in the java.util.function package.
Lastly but not least, in terms of refactoring it's not always possible to use method references nor is it always better to use lambdas expressions over code that we used prior to Java-8. However, as you go on with your journey of learning the Java-8 features, a few tips to better your code are to try:
Refactoring anonymous classes to lambda expressions
Refactoring lambda expressions to method references
Refactoring imperative-style data processing to streams

Is it possible to initialize `List` with a value on method call

below is a method with argument of type List
void addToMe(List<String> list)
is it possible to pass value like this
addToMe(new ArrayList<String>().add("Test Element"));
Right now giving me error incompatible types: boolean cannot be converted to List<String>. Why it is not allowed ?
The reason you can't do addToMe(new ArrayList<String>().add("Test Element")); is because add(E) isn't designed for fluent style programming: it returns a boolean, not the original list with an added element.
Option 1: Anonymous type with initializer block*
new ArrayList<String>(){{ add("Test Element"); }}
In your code:
void addToMe(new ArrayList<String>(){{ add("Test Element"); }})
This is read+write and still a List<String> so you can treat it as such without problems, (warning: there are subtle consequences to this, as the type returned by getClass() on that list won't be java.util.ArrayList now, it will instead be an anonymous class type).
Option 2: use the (dangerous) Arrays.asList(String... args)
Alternatively if you only need a List<String>, and it doesn't need to be writeable you could use Arrays#asList. But: be careful. This is a fixed size list. Adding more items to it will throw an UnsupportedOperationException:
void addToMe(Arrays.asList("Test Element1", "Test Element2"));
Option 3: use the JCF "copy" constructor
In the documentation for Collection<E> in the JCF, it states:
All general-purpose Collection implementation classes ... should provide ... a constructor with a single argument of type Collection, which creates a new collection with the same elements as its argument.
Therefore, if you use this with Arrays.asList(String... args), you can do:
void addToMe(new ArrayList<String>(Arrays.asList("Test Element1", "Test Element2")));
But this is ugly and hard to read, so I recommend splitting it up into more than one line. The advantage over just using Arrays.asList is that it is read+write and not fixed size.
addToMe(new ArrayList().add("Test Element"));
The .add() method of a List is inherited from Collection, and by definition it does not return the Collection itself but a boolean; therefore this can't work.
You can, however, do:
addToMe(Arrays.asList("Test Element"));
The Java compiler will never "automagically" walk the method call chain and deduce your intents; it is pretty "dumb".
But on the other hand, everybody has different intents; this is why languages have rules... And languages which are too "liberal" often are hard to read.
As one of the Java core specification writer once said: "You write code only once; you read it many times".
your call to "add(Element)" returns true on success or false on failure. It's a boolean function. Instead, use:
addToMe(new ArrayList<String>(new String[]{"Test Element"}));
You are passing a boolean into your function, because that is what List.add() returns. You can see the Java documentation for List.add() here:
http://docs.oracle.com/javase/7/docs/api/java/util/List.html#add(E)

Difference between byte[] and byte ... in Java Methods

Someone asked me what the difference between the two method parameters and why you would use the ... over specifically assigned array.
putMessage(byte ...send)
putMessage(byte[] send)
I couldn't answer them with confidence and couldn't remember what the ... is called.
The ... in your first example are called varargs. Your second example has an array argument. Varargs are a convenience for times when you want to hard code a variable number of arguments to a method but don't want to manually create an array to hold them. It's a shorthand notation. Consider this:
putMessage(0b00100101, 0b00100101, 0b00100101); // varargs
vs. this:
putMessage(new byte[] { 0b00100101, 0b00100101, 0b00100101 }); // array
The first example is less cluttered and more readable.
The parameters with ellipses are generally referred to as "varargs" if you want to google that.
Using varargs allows you to call a method with variable number of arguments without having to specify an array e.g.
public void printStr(String ...strings) {
for (String s : strings) {
System.out.println(s);
}
}
> printStr("Hello", "World")
Hello
World
So varargs allow a certain degree of convenience, but there are downsides - the varargs parameter must be the last parameter in the method signature, and thus you cannot have more than one varargs parameter to a method. If you want to pass multiple arrays to a method you have to use arrays, not varargs.
Another reason you might see arrays in some places where you might expect varargs is that varargs were only introduced in Java 5 - older code and code that needs to be backwards compatible will still be using arrays even where it might make more sense conceptually to use varargs.
The advantage of using varargs in the method signature is flexibility - there are some situations where the caller will have an array ready anyway and some where they will just have several arguments. Varargs will accept either the array or each variable as a separate argument, saving the caller the trouble of instantiating and populating an array.
The first one is with Varargs.
In short
A. First can be used to call with single byte type arg, 2 byte args.. or many args or an array.
B. second will be used with array only.
The ellipsis (three dots) indicates that you are using "varargs".
See http://download.oracle.com/javase/1,5.0/docs/guide/language/varargs.html for more details.
Inside the method, you access the elements of "send" as an array. The two methods are the same in that regard. The convenience is for the caller. In the second putMessage, the caller is compelled to create an array of bytes to pass to putMessage. In the first putMessage, the caller can simply say "putMessage(byte1, byte2)" or "putMessage(byte1, byte2, byte3)" or "putMessage(byte1)" -- variable number of arguments, or varargs.
The ellipses (...) allow you to inline N parameters of a type to a function call without having to define an array first. In the end you do simply get an array of parameters but it's basically shorthand or syntactic sugar. Also your client code might be a little cleaner and more declarative with the ellipses syntax... though it could easily go the other way and become mucky and unreadable.
Here's a great example of the ellipses syntax (variable length argument lists.) While looking at the sample consider what the client code (in the main function) would look like if an array was used instead of a variable length argument list.

JSR223: Calling Java "varargs" methods from script

I have a method that looks like this on Java:
public void myMethod(Object... parms);
But I can't call this method as expected from the scripts.
If, in ruby, I do:
$myObject.myMethod(42);
It gives me org.jruby.exceptions.RaiseException: could not coerce Fixnum to class [Ljava.lang.Object
If I try the following in Javascript:
myObject.myMethod(42);
Then it gives me sun.org.mozilla.javascript.internal.EvaluatorException: Can't find method MyClass.test(number). (#2) in at line number 2
Of course, if I change the signature to take one single object then it works.
I assume that this is because someone along the line does not know how to convert, say Integer to Integer[] with the value at the first position.
I believe something like myMethod({42, 2009}) would work in Ruby, but this seems ugly - I wanted to be able to just do myMethod(42, 2009) to make it less confusing, specially for other languages. Is there any better workaround for this?
Thanks.
Java internally treats the variable-length argument list as an array whose elements are all of the same type. That is the reason why you need to provide an array of objects in your JRuby script.
It works like this:
myMethod [42, 2009].to_java
The to_java method constructs a Java array from a Ruby array. By default, to_java constructs Object arrays as needed in this case. If you need a String array you would use
["a","b","c"].to_java(:string)
More on this at the JRuby wiki
It seems like this is a known bug in jruby. See method dispatch on Java objects / classes should try to find a matching varargs method and NameError thrown when trying to pass argument to a Java method that is defined as having variable length arguments.
According to the link Rhino does support vararg.
Varargs are handled by the compiler as an Object[] which is what the error message describes.
I do not have JRuby experience, but does it work if you have an array argument?

Categories

Resources