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.
Related
I am trying to implement something that will replace ${pattern} in xml files in my build.gradle file:
processResources {
eachFile { FileCopyDetails fileCopyDetails ->
if (fileCopyDetails.name.contains("blueprint.xml")) {
project.logger.quiet "Processing: " + fileCopyDetails.path
logger.quiet "" + project.ext.properties.entrySet()
filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [prop1, value, prop2, value])
}
}
}
tokens: appears to take a Map. Again, how does this relate to the function signature?
Convert all properties with String values into a Map for input into tokens:
def tokenMap = new LinkedHashMap()
def stringProps = project.ext.properties.entrySet().findAll { entry -> entry.getValue() instanceof String }
stringProps.each { entry -> tokenMap.put(entry.key, entry.value)}
A look at the Gradle Javadoc reveals a filter function whose signature does not seem to match the examples. Particularly, the Map<String,?> and Class<? extends FilterReader> do not match the order in the examples as far as I understand them. Could somebody map the example to the function signature so that I understand what is going on?
CopySpec filter(Map<String,?> properties,
Class<? extends FilterReader> filterType)
Adds a content filter to be used during the copy. Multiple calls to
filter, add additional filters to the filter chain. Each filter should
implement java.io.FilterReader. Include org.apache.tools.ant.filters.*
for access to all the standard Ant filters.
Filter properties may be specified using groovy map syntax.
Examples:
filter(HeadFilter, lines:25, skip:2)
filter(ReplaceTokens, tokens:[copyright:'2009', version:'2.3.1'])
Specified by:
filter in interface ContentFilterable
Parameters:
properties - map of filter properties
filterType - Class of filter to add
Returns:
this
Related:
How is a token replaced in a file for a Gradle build product?
Note:
What does not work is the SimpleTemplateEngine
processResources {
filesMatching("**/features.xml") {
// expand uses Groovy's SimpleTemplateEngine
project.logger.quiet "Processing: " + file
expand project.getProperties()
}
This is actually a feature of the underlying Groovy syntax. Groovy allows you to specify method parameters by name (i.e. <name>: <value>) whenever the first parameter to the method is declared as a Map. Crucially, named parameters can appear at any point in the argumemt list, even after so-called positional parameters (i.e. those declared after the Map in the method signature), and they will be put as entries in the initial Map parameter. See the section Mixing named and positional parameters in the Groovy Language Documentation for more details.
So, the Gradle filter method has signature
CopySpec filter(Map<String,?> properties, Class<? extends FilterReader> filterType)
The first properties parameter is of type Map, so this method can be called with named parameters. In addition, there is one more positional parameter, filterType. So, to call this method, you must specify one parameter without a name, of type Class<? extends FilterReader>, which will be used for filterType, and zero or more named parameters, which will all be put in the properties Map.
Taking one of the examples from the documentation:
filter(HeadFilter, lines:25, skip:2)
will mean that filter is called with
properties = [
lines: 25,
skip: 2
]
filterType = HeadFilter
Any of the following calls would be equivalent:
filter(lines:25, skip:2, HeadFilter)
filter(lines:25, HeadFilter, skip:2)
filter([lines:25, skip:2], HeadFilter)
The last call here passes both parameters positionally (you don't have to use named parameters when the first parameter is declared as a Map).
Aside Note
I'm intrigued as to why using expand doesn't work - it should!
I have one void method that does something on the object passed as a param
public void someMethod(Object object) {
...
}
and I want to invoke it in other method - but only when the one of the Object attributes is not null.
public void otherMethod(Number number) {
repository.findObject(number)
.filter(o -> o.getAttributeOne().isPresent())
.ifPresent(o -> performThirdMethod(o));
}
I want to make sure that performThirdMethod() will be only invoked when one of the object attribute (attributeOne) is NOT null.
Is above method correct? I also wonder what would be the best way to test if this actually works?
In other words - how (in Java8) I can filter the result to see if the attribute is present - if in one method I am passing a whole object and want to check only one attribute presence that belongs to it?
From the Javadoc of Optional.ifPresent(Consumer)
If a value is present, performs the given action with the value, otherwise does nothing.
Since it's a lambda o -> performThirdMethod(o), not a direct invocation of performThirdMethod, you are given a guarantee that the method "will be only invoked when one of the object attribute (attributeOne) is NOT null".
I also wonder what would be the best way to test if this actually works?
I use a debugger with a breakpoint inside a lambda. You also can log a message before calling performThirdMethod.
.ifPresent(o -> {
System.out.println("performThirdMethod will be executed");
performThirdMethod(o);
});
Since Java 9, there is ifPresentOrElse(Consumer,Runnable) to perform a empty-based action if no value is present.
.ifPresentOrElse(
o -> performThirdMethod(o),
() -> System.out.println("performThirdMethod won't be executed")
);
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()));
I am working on generics and found that the following code is giving compile time error at comparing method.
Multiple markers at this line
- Cannot infer type argument(s) for comparing(Function)
- The type A does not define m1(Object) that is applicable here
class A<T> {
String m1() {
return null;
}
}
class B {
void test() {
Comparator<A<String>> target = Comparator.comparing(A::m1).thenComparing(A::m1);
}
}
Can some one help me understand this behavior; and how can I fix the problem?
If you specify the exact generic types at the comparing method, the code compiles.
Comparator<A<String>> target =
Comparator.<A<String>, String>comparing(A::m1).thenComparing(A::m1);
You should specify type parameter for class A.
Comparator<A<String>> target = Comparator.comparing(A<String>::m1).thenComparing(A<String>::m1);
Interesting question. Haven't gone into JLS but I guess type inference does not work in case of chained method call. (You can see it works for a simple Comparator<A<String>> target = Comparator.comparing(A<String>::m1); )
One quick fix, which is similar to another answer, is help Java do the type inference:
Comparator<A<String>> target = Comparator.comparing(A<String>::m1)
.thenComparing(A::m1);
Please note doing it at the first method already do the trick.
(Looking forward to see if someone can dig out JLS to see if such inference is supposed to be valid :P )
you can nested as
Comparator<A<String>> target1 = Comparator.comparing(A::m1);
Comparator<A<String>> target2 = target1.thenComparing(A::m1);
myVarList.sort(target2);
Comparator<A<String>> target = Comparator.comparing(A::m1).thenComparing(A::m1);
thenComparing() expects a Comparator object as parameter...
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());