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")
);
Related
Is there a way to detect usages of java method reference (double colon) operator inside the code?
I need to discover all instance/static method references used in a given class in order to be able to detect some errors (must verify that the target method has a particular annotation - #Good in the below example) during build time. As by convention a method reference should be used only to some of the methods when it is passed to a constructor of some helper class (Info in the below example).
class X {
Info init() {
return new Info(X::beta); // good code: target method has #Good annotation
return new Info(X::alpha); // bad code: target method has no #Good annotation
}
void alpha() {
}
#Good
void beta() {
}
}
The intention is to be able to click on the method reference as this makes it easy to follow as otherwise if just passing Method instance or just method name it would lack this ability.
(The example is not very good but I'm now allowed to share more details, sorry about that!)
I can see IntelliJ IDEA "knows" about them - when you ctrl+click on them it navigates to the target method so there should be some form of a static analysis used there.
I'm already using ObjectWeb ASM to detect invocations to certain methods but it seems it lacks the ability to detect method references (::)
EDIT:
Just a note that you can also pass new Info(x -> x.alpha()) as #Thomas below mentioned in the comments but this would not pass our review process but I guess the additional ability to detect it would not hurt.
EDIT2: What exactly are you trying to achieve with these checks? What makes beta worthy of receiving the annotation?
Answer:
When the init() method is called we obtain the Info instance and from it obtain the lambda which must be a method reference. Then we use javassist ProxyFactory and create a sub-class of class X then instantiate it and intercept all its methods via setting a method handler. So now it is safe to execute the lambda without allowing it to make any side effects - the method body is skipped and the only thing we do is to capture which is the X method that the lambda actually is calling - in the example this will lead to a java.lang.Method instance pointing to X.beta or X.alpha method. Then we can check if it has the #Good annotation and proceed accordingly - which is to call the lambda without any proxying, but that call might happen later, like a millisecond later or an hour later. If there is no #Good annotation we cannot proceed - it is a bug.
So the problem is that this will happen at runtime later and there might be a bug not caught early enough and that is the reason I would like to inspect the X class at build time and catch all the bugs :)
This is a bit of a shot in the dark, as I'm neither very proficient with ASM nor sure if this approach addresses your problem. Having said that, I found that, in a similar setting, asm.MethodVisitor calls MethodVisitor.visitInvokeDynamicInsn(...) for (some? all?) method references.
E.g., if I compile this variant of your class X along with an Info:
class Info {
public Info(Runnable alpha) {}
}
class X {
Info init() { return new Info(this::alpha); }
void alpha() {}
}
... and I then feed the resulting X.class into a mini ClassVisitor + printing MethodVisitor (Groovy for brevity):
class MyMethodVisitor extends MethodVisitor {
MyMethodVisitor(MethodVisitor parent) { super(Opcodes.ASM8, parent) }
#Override
void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {
println "visitInvokeDynamicInsn($name, $descriptor, $bootstrapMethodHandle, $bootstrapMethodArguments)"
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments)
}
}
class MyClassVisitor extends ClassVisitor {
MyClassVisitor() { super(Opcodes.ASM8) }
#Override
MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
println "Starting method '$name'"
new MyMethodVisitor(super.visitMethod(access, name, descriptor, signature, exceptions))
}
}
def clr = new ClassReader(new File("./X.class").bytes)
clr.accept(new MyClassVisitor(), ClassReader.SKIP_FRAMES)
Then the method visitor prints, amongst other details, a call to visitInvokeDynamicInsn from within the method visitation of X::init with the desired X::alpha among the arguments (the xyz being my local package):
Visiting method '<init>'
Visiting method 'init'
visitInvokeDynamicInsn(run, (xyz/X;)Ljava/lang/Runnable;,
java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; (6),
[()V, xyz/X.alpha()V (5), ()V])
Visiting method 'alpha'
So it would seem possible to peel the method out of those arguments. I am not sure if this reliable (e.g., whether this bytecode is guaranteed by specification, or whether it can depend on compilation/optimization details).
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.
I am very new to lambdas in Java.
I have started using it as i found them quite interesting
but i still don't know how to use them completely
I have a list of uuids and for each uuid i want to call a function which takes two parameters : first is a string and second is uuid
I am passing a constant string for each uuid
I have written a following code but its not working
uuids.stream()
.map(uuid -> {"string",uuid})
.forEach(AService::Amethod);
It is method which is another class AService
public void Amethod(String a, UUID b) {
System.out.println(a+b.toString());
}
A lambda expression has a single return value, so {"string",uuid} doesn't work.
You could return an array using .map(uuid -> new Object[]{"string",uuid}) but that won't be accepted by your AService::Amethod method reference.
In your example you can skip the map step:
uuids.stream()
.forEach(uuid -> aservice.Amethod("string",uuid));
where aservice is the instance of AService class on which you wish to execute the Amethod method.
uuids.stream().forEach(uuid -> AService.Amethod("string", uuid));
You can write something closer to your current code given a Pair class, but 1) you end up with more complicated code; 2) Java standard library doesn't have one built-in. Because of 2), there are quite a few utility libraries which define one. E.g. with https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html, it would be
uuids.stream()
.map(uuid -> Pair.of("string",uuid))
.forEach(pair -> AService.Amethod(pair.getLeft(), pair.getRight()));
I was reading the new features of Log4j2 and there's a feature that enables
"Java 8 lambda support for lazy logging"
And it gives two examples
The first one is the bad practice
// pre-Java 8 style optimization: explicitly check the log level
// to make sure the expensiveOperation() method is only called if necessary
if (logger.isTraceEnabled()) {
logger.trace("Some long-running operation returned {}", expensiveOperation());
}
And the second one is the good practice
// Java-8 style optimization: no need to explicitly check the log level:
// the lambda expression is not evaluated if the TRACE level is not enabled
logger.trace("Some long-running operation returned {}", () -> expensiveOperation());
Where is being made the checking if the requested log level is enabled ?
"logger.isTraceEnabled()" ?
Where is being made the checking if the requested log level is enabled ?
Inside the logger.trace() method.
The trick here however is in the way you pass the argument. Pre-java8 style computed the value at the time of calling logger.trace.
logger.trace(..., expensiveOperation());
Java 8 style uses a Supplier
logger.trace( ..., () -> expensiveOperation());
So the expensiveOperation() is called only when requested - inside the trace method.
Have a look at implementation of java.util.logging.Logger.log():
public void log(Level level, Supplier<String> msgSupplier) {
if (!isLoggable(level)) {
return;
}
LogRecord lr = new LogRecord(level, msgSupplier.get()); // <-- here is the expensive computation
doLog(lr);
}
The trace method (or any other logging method for that sake) already checks logging level internally. Checking it in the caller too is an optimization to avoid calculating expensiveOperation(). With Java 8's new syntax, we don't pass the calculated value of expensiveOperation(), but a lambda that invokes it only if needed.
Note that although log4j didn't implement it, you could, in theory, have the same behavior without the fancy Java 8 syntax by defining an interface for a value provider, and have the logging method call it:
// Interface definition
public interface ValueProvider {
String getValue();
}
// Calling it from the main code with an anonymous implementation:
logger.trace("Some long-running operation returned {}",
new ValueProvider() {
public String getValue() {
return expensiveOperation();
}
});
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());