Using Method Cast instead of Lambda on List with Objects - java

I am working on fixing some Sonar - Codesmells in my project at the moment.
Sonar is pointing me at this method:
protected List<Long> rolesIdsFromPortalSpecificAuthorizations(final List<PortalSpecificAuthorization> portalSpecificAuthorizations) {
return portalSpecificAuthorizations.stream().map(portalSpecificAuthorization -> portalSpecificAuthorization.getId()).collect(Collectors.toList());
}
It says:
Replace this lambda with a method reference.
The PortalSpecificAuthorization is an object from my own coding. I was not able to find an easy-to-use solution for this case with Method casts.
What is the correct usage in this case?

Sonar wants a Method Reference instead of the lambda.
Replace this part:
portalSpecificAuthorization -> portalSpecificAuthorization.getId()
with this
PortalSpecificAuthorization::getId
More about Method References: https://www.javatpoint.com/java-8-method-reference
Full Code:
protected List<Long> rolesIdsFromPortalSpecificAuthorizations(final List<PortalSpecificAuthorization> portalSpecificAuthorizations) {
return portalSpecificAuthorizations.stream()
.map(PortalSpecificAuthorization::getId)
.collect(Collectors.toList());
}

Related

Unused method parameters should be removed (squid:S1172) false positive using lambda

I have a working method that uses parameter as a lambda expression
private BiConsumer<List<String>, Properties> handleList(Properties p) {
return (list, prop) -> executeSubList(list, prop);
}
For p I'm getting a false positive warning from SonarLint
Unused method parameters should be removed (squid:S1172)
If I change prop to p I'm getting a compile error
Lambda expression's parameter p cannot redeclare another local variable defined in an enclosing scope
Is there a real issue or is it a false positive check when using a method parameter as a lambda parameter?
The issue is real. This method returns a BiConsumer that runs executeSubList on a pair or List<String> and Properties arguments, but does so regardless of p. You could just remove it:
private BiConsumer<List<String>, Properties> handleList() {
// p was never used, and can just be removed -------^
return (list, prop) -> executeSubList(list, prop);
}
The accepted answer is totally correct.
However, another take on solving this problem is to create a Consumer instead of a BiConsumer. The parameter p would then be used as the parameter to executeSubList():
private Consumer<List<String>> handleList(Properties p) {
return (list) -> executeSubList(list, p);
}
Whether this or the solution provided in the accepted answer is the best way to go is dependant on how the surrounding code looks and how the method is to be used.

SonarLint: Replace this lambda with a method reference

I have a collection that contains a list of errors. I wanted to group these by a key (UUID UserId). For this I have copied the code from this answer:
https://stackoverflow.com/a/30202075/4045364
Collection<FilterError> filterErrors = new ArrayList<FilterError>();
// ... some filterErrors get added to the collection ...
return filterErrors.stream().collect(Collectors.groupingBy(w -> w.getUserId()));
Sonar Lint gives me the following error:
Replace this lambda with a method reference. ->
What I have tried:
Based on these question: SONAR: Replace this lambda with a method reference and Runable Interface : Replace this lambda with a method reference. (sonar.java.source not set. Assuming 8 or greater.)
filterErrors.stream().collect(Collectors.groupingBy(this::getUserId()));
Based on this question: Replace this lambda with method reference 'Objects::nonNull'
filterErrors.stream().collect(Collectors.groupingBy(UUID::getUserId()));
Both give the error:
The target type of this expression must be a functional interface
Is there a way I can resolve this SonarLint issue?
You need to use the class name of the object being targeted by the stream.
Example:
List<String> list = ...;
list.stream().collect(Collectors.groupingBy(String::toUpperCase));
so in your case:
FilterError::getUserId
In my case previously it was like this -
whitelist0.stream().filter(whitelistEntry -> !whitelistEntry.isEmpty()).map(s -> WhitelistEntry.of(s)).collect(Collectors.toList()));
As I need to pass a value to the function, so I did the following to replace the lambda with method reference -
whitelist0.stream().filter(whitelistEntry -> !whitelistEntry.isEmpty()).map(WhitelistEntry :: of).collect(Collectors.toList()));

Java Eclipse Lambda function error

The following code produces an error for me in Eclipse Luna and prevents from running.
jdbcTemplate.query( query, r -> {
Folder folder = new Folder();
folder.setId(r.getLong("id"));
folder.setFolderName(r.getString("folder_name"));
folder.setFullPath(r.getString("full_path"));
folders.add(folder);
}, folderId);
return folders;
}
The error occurs at jdbcTemplate.query
The method query(String, ResultSetExtractor<Object>, Object[]) is ambiguous for the type JdbcTemplate
Error does not occur when I package file as jar and run.
Thanks for any guidance or suggestions.
There are three query methods in JdbcTemplate, as follows:
query(String, ResultSetExtractor<T>);
query(String, RowCallbackHandler);
query(String, RowMapper<T>);
In all three, the second argument can be implemented as a lambda, and the two that have a generic <T> parameter will return something. Eclipse is complaining that it can't tell the difference between the first and the third method you are referring to, so you need to specify the type of the functional interface that the lambda is replacing. Additionally, as Jack Ammo says, you need to return an object from the lambda to satisfy the interface. Don't add the returned object to your list of folders inside the lambda, either. So, you'd be looking at something like this:
Folder folder = jdbcTemplate.query( query, (ResultSetExtractor<Folder>) r -> {
Folder folder = new Folder();
folder.setId(r.getLong("id"));
folder.setFolderName(r.getString("folder_name"));
folder.setFullPath(r.getString("full_path"));
return folder;
}, folderId);
folders.add(folder);
return folders;
according to the javadoc for ResultSetExtractor, the method you're implementing with your lambda expression is Object extractData(ResultSet rs). extractData returns an Object so your lambda also has to return an Object in order to properly implement it.

Create Java8 function reference programmatically

Just a theoretic question, I do not have practical use-case currently.
Assuming some my API accepts function reference as an argument and I would like to both feed it directly from code via '::' syntax or collect matching functions via reflection, store in some Map and invoke conditionally.
It is possible to programmatically convert method into Consumer<String>?
Map<String, Consumer<String>> consumers = new HashMap<>();
consumers.put("println", System.out::println);
Method method = PrintStream.class.getMethod("println", String.class);
consumers.put("println", makeFunctionReference(method));
...
myapi.feedInto(consumers.get(someInput.getConsumerId()));
Update:
Though not satisfied by solutions in currently provided answers, but after getting the hint about LambdaMetaFactory I tried to compile this code
public class TestImpl {
public static void FnForString(String arg) {}
}
public class Test {
void test() {
List<String> strings = new ArrayList<>();
Consumer<String> stringConsumer = TestImpl::FnForString;
strings.stream().forEach(stringConsumer);
strings.stream().forEach(TestImpl::FnForString);
stringConsumer.accept("test");
}
}
and after feeding only Test class into CFR decompiler I'm getting following back:
public class Test {
void test() {
ArrayList strings = new ArrayList();
Consumer<String> stringConsumer =
(Consumer<String>)LambdaMetafactory.metafactory(
null, null, null,
(Ljava/lang/Object;)V,
FnForString(java.lang.String),
(Ljava/lang/String;)V)();
strings.stream().forEach(stringConsumer);
strings.stream().forEach(
(Consumer<String>)LambdaMetafactory.metafactory(
null, null, null,
(Ljava/lang/Object;)V,
FnForString(java.lang.String ),
(Ljava/lang/String;)V)());
stringConsumer.accept("test");
}
}
Out of that I see that:
This is somehow possible to do in '1-liner' manner
No exception handling is required
I have no idea what is (Ljava/lang/Object;)V (and others) in decompiler's output. It should match to MethodType in metafactory() arguments. Additionally - either decompiler 'eats/hides' something, but there seems to be now invocations of methods during getting of function reference.
(offtop) Obtaining function reference even in compiled code is at least one function call - in general this may be not unnoticeably cheap operation in performance critical code.
And ... with both Test and TestImpl classes provided, CFR reconstructs absolutely same code that I've compiled.
You could do this with reflection like this:
consumers.put("println", s -> {
try {
method.invoke(System.out, s);
} catch (InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
});
But it you want your code to compile to the same code using a method-reference (i.e. using invokedynamic instructions), you can use a MethodHandle. This does not have the overhead of reflection and so it will perform a lot better.
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType methodType = MethodType.methodType(void.class, String.class);
MethodHandle handle = lookup.findVirtual(PrintStream.class, "println", methodType);
consumers.put("println", s -> {
try {
handle.invokeExact(System.out, s);
} catch (Throwable e) {
throw new RuntimeException(e);
}
});
consumers.get("println").accept("foo");
In this code, first a MethodHandles.Lookup object is retrieved. This class is reponsible for creating MethodHandle objects. Then a MethodType object, which represents the arguments and return type accepted and returned by a method handle is created: in this case, it is a method that returns void (hence void.class) and takes a String (hence String.class). Finally, the handle is obtained by finding the println method on the PrintStream class.
You can refer to this question (and this one) for more information about what MethodHandles are.
The simplest, albeit not necessarily most performant, approach would be just wrapping the Method into a Consumer.
final Method m = ...
final T target = ...
Consumer<String> c = (arg1) => m.invoke(t, arg1);
Using the LambdaMetaFactory may yield more optimal code, but considering that you're dispatching through a Map it's probably not worth it.
This is somehow possible to do in '1-liner' manner
If you really want to emulate what the bytecode does that's only true for for sufficiently tortured definitions of one-liner. Your decompiler lies to you to some extent.
No exception handling is required
That is because the concept of checked exceptions does not exist on the bytecode level. This can be emulated with static helper methods that do a sneaky rethrow for you.
I have no idea what is (Ljava/lang/Object;)V (and others) in decompiler's output. It should match to MethodType in metafactory() arguments. Additionally - either decompiler 'eats/hides' something, but there seems to be now invocations of methods during getting of function reference.
It looks like pseudocode for invokedynamic calls. What the JVM really does is more complicated and can't be expressed concisely in java since it involves lazy initialization. It's best to read the java.lang.invoke package description to get an idea what really happens.
The java-level equivalent to the linking stage would be putting the CalleSite's dynamicInvoker MH into a static final MethodHandle field and calling its invokeExact method.
(offtop) Obtaining function reference even in compiled code is at least one function call - in general this may be not unnoticeably cheap operation in performance critical code.
as mentioned above, the linking stage is equivalent to putting the methodhandle in a static field once and then calling that in the future instead of attempting to resolve the method a second time.
The decompiler failed badly on your code, however, there is no correct decompiling anyway, besides recreating the original Java 8 method reference, which is not what you were interested in.
The lambda expressions and method references are compiled using an invokedynamic byte code instruction which has no equivalent in the Java programming language. The equivalent code would be something like:
public static void main(String... arg) {
Consumer<String> consumer=getConsumer();
consumer.accept("hello world");
}
static Consumer<String> getConsumer() {
try {
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodType consumeString = MethodType.methodType(void.class, String.class);
return (Consumer<String>)LambdaMetafactory.metafactory(lookup, "accept",
MethodType.methodType(Consumer.class, PrintStream.class),
consumeString.changeParameterType(0, Object.class),
lookup.findVirtual(PrintStream.class, "println", consumeString), consumeString)
.getTarget().invokeExact(System.out);
}
catch(RuntimeException | Error e) { throw e; }
catch(Throwable t) { throw new BootstrapMethodError(t); }
}
except, that everything that is done within getConsumer() is originally handled by a single invokedynamic instruction which will treat all involved MethodHandles and MethodType instances like constants and whose result of the first-time evaluation gets an intrinsic caching facility. You can’t model that using ordinary Java source code.
Still, the Consumer<String> returned by the getConsumer() method above is the exact equivalent of the expression System.out::println (when assigned to a Consumer<String>) bearing the same behavior and performance characteristics.
You may study “Translation of Lambda Expressions” by Brian Goetz for getting a deeper understanding of how it works. Also, the API documentation of LambdaMetafactory is quite exhaustive.

Proper usage of Optional.ifPresent()

I am trying to understand the ifPresent() method of the Optional API in Java 8.
I have simple logic:
Optional<User> user=...
user.ifPresent(doSomethingWithUser(user.get()));
But this results in a compilation error:
ifPresent(java.util.functionError:(186, 74) java: 'void' type not allowed here)
Of course I can do something like this:
if(user.isPresent())
{
doSomethingWithUser(user.get());
}
But this is exactly like a cluttered null check.
If I change the code into this:
user.ifPresent(new Consumer<User>() {
#Override public void accept(User user) {
doSomethingWithUser(user.get());
}
});
The code is getting dirtier, which makes me think of going back to the old null check.
Any ideas?
Optional<User>.ifPresent() takes a Consumer<? super User> as argument. You're passing it an expression whose type is void. So that doesn't compile.
A Consumer is intended to be implemented as a lambda expression:
Optional<User> user = ...
user.ifPresent(theUser -> doSomethingWithUser(theUser));
Or even simpler, using a method reference:
Optional<User> user = ...
user.ifPresent(this::doSomethingWithUser);
This is basically the same thing as
Optional<User> user = ...
user.ifPresent(new Consumer<User>() {
#Override
public void accept(User theUser) {
doSomethingWithUser(theUser);
}
});
The idea is that the doSomethingWithUser() method call will only be executed if the user is present. Your code executes the method call directly, and tries to pass its void result to ifPresent().
In addition to #JBNizet's answer, my general use case for ifPresent is to combine .isPresent() and .get():
Old way:
Optional opt = getIntOptional();
if(opt.isPresent()) {
Integer value = opt.get();
// do something with value
}
New way:
Optional opt = getIntOptional();
opt.ifPresent(value -> {
// do something with value
})
This, to me, is more intuitive.
Why write complicated code when you could make it simple?
Indeed, if you are absolutely going to use the Optional class, the most simple code is what you have already written ...
if (user.isPresent())
{
doSomethingWithUser(user.get());
}
This code has the advantages of being
readable
easy to debug (breakpoint)
not tricky
Just because Oracle has added the Optional class in Java 8 doesn't mean that this class must be used in all situation.
You can use method reference like this:
user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);
Method ifPresent() get Consumer object as a paremeter and (from JavaDoc): "If a value is present, invoke the specified consumer with the value." Value it is your variable user.
Or if this method doSomethingWithUser is in the User class and it is not static, you can use method reference like this:
user.ifPresent(this::doSomethingWithUser);
Use flatMap. If a value is present, flatMap returns a sequential Stream containing only that value, otherwise returns an empty Stream. So there is no need to use ifPresent() . Example:
list.stream().map(data -> data.getSomeValue).map(this::getOptinalValue).flatMap(Optional::stream).collect(Collectors.toList());

Categories