You have forgotten the generics on your BiFunction:
public static void main(final String[] args) throws Exception {
final Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);
final BiFunction<String, Integer, Integer> remapper = (k, v) -> v == null ? 42 : v + 41;
map.compute("A", remapper);
}
Running:
PS C:\Users\Boris> java -version
java version "1.8.0-ea"
Java(TM) SE Runtime Environment (build 1.8.0-ea-b120)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b62, mixed mode)
Answer from Boris the Spider on Stack OverflowYou have forgotten the generics on your BiFunction:
public static void main(final String[] args) throws Exception {
final Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);
final BiFunction<String, Integer, Integer> remapper = (k, v) -> v == null ? 42 : v + 41;
map.compute("A", remapper);
}
Running:
PS C:\Users\Boris> java -version
java version "1.8.0-ea"
Java(TM) SE Runtime Environment (build 1.8.0-ea-b120)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b62, mixed mode)
As Boris The Spider points out, the specific problem you have is the generics. Depending on context, adding an explicit {} block around the lambda will help add clarity and may be required.
With that, you would get something like:
BiFunction<String, Integer, Integer> x = (String k, Integer v) -> {v == null ? 42 : v + 41};
It would be pretty helpful for future readers who have the same problem if you posted your syntax errors so they can be indexed.
This link has some additional examples that might help.
The answer is simple: you can't (directly) do that.
A very ugly hack would be to mutate an external object instead of assigning to a variable:
Optional<String> optStr = Optional.of("foo");
StringBuilder sb = new StringBuilder();
optStr.ifPresent(s -> sb.append(s));
String result = sb.toString();
This works because sb is effectively final here
But I want to emphasize the point that this is probably not what you really want to do.
- If you want to have a default value, use
optStr.orElseoroptStr.orElseGetinstead. - If you want to map the result only if it is present, use
optStr.map
Since you did not provide your whole use-case, these are just guesses but the key point is that I really do not recommend the above snippet of code: it goes against the concept of functional programming (by mutating an object).
Another way, similar to what Tunaki has written, is to use a single-cell table:
Optional<String> optStr = Optional.of("foo");
String[] temp = new String[1];
optStr.ifPresent(s -> temp[0] = s);
String result = temp[0];
The table object is final, what changes is its content.
Edit: A word of warning though - before using this hacky solution check out the other answers to OP's question, pointing out why it's a bad idea to use this workaround and consider if it's really worth it!
Use a wrapper
Any kind of wrapper is good.
With Java 10+, use this construct as it's very easy to setup:
var wrapper = new Object(){ int ordinal = 0; };
list.forEach(s -> {
s.setOrdinal(wrapper.ordinal++);
});
With Java 8+, use either an AtomicInteger:
AtomicInteger ordinal = new AtomicInteger(0);
list.forEach(s -> {
s.setOrdinal(ordinal.getAndIncrement());
});
... or an array:
int[] ordinal = { 0 };
list.forEach(s -> {
s.setOrdinal(ordinal[0]++);
});
Note: be very careful if you use a parallel stream. You might not end up with the expected result. Other solutions like Stuart's might be more adapted for those cases.
For types other than int
Of course, this is still valid for types other than int.
For instance, with Java 10+:
var wrapper = new Object(){ String value = ""; };
list.forEach(s->{
wrapper.value += "blah";
});
Or if you're stuck with Java 8 or 9, use the same kind of construct as we did above, but with an AtomicReference...
AtomicReference<String> value = new AtomicReference<>("");
list.forEach(s -> {
value.set(value.get() + s);
});
... or an array:
String[] value = { "" };
list.forEach(s-> {
value[0] += s;
});
This is fairly close to an XY problem. That is, the question being asked is essentially how to mutate a captured local variable from a lambda. But the actual task at hand is how to number the elements of a list.
In my experience, upward of 80% of the time there is a question of how to mutate a captured local from within a lambda, there's a better way to proceed. Usually this involves reduction, but in this case the technique of running a stream over the list indexes applies well:
IntStream.range(0, list.size())
.forEach(i -> list.get(i).setOrdinal(i));
For example:
BiFunction<Number,Number,Number> sum = (value1, value2) -> value1 + value2;
and you can use it like this:
sum.apply(value1,value2)
more examples here.
Also you can store your set of BiFunction in a Map:
Map<String,BiFunction<Number, Number, Number>> operations = new HashMap<>();
operations.put("sum", sum);
If you do the same for all operations, you call them as it follows:
operations.get("sum").apply(value1, value2);
So, the result can be computed as is follows:
result = (value1, value2, operation) -> operations.get(operation).apply(value1, value2);
You can create a map with a String and DoubleBinaryOperator or an IntBinaryOperator depends on the type you want like so :
Map<String, DoubleBinaryOperator> map = new HashMap<>();
map.put("add", (v1, v2) -> v1 + v2);
map.put("mult", (v1, v2) -> v1 * v2);
map.put("div", (v1, v2) -> v1 / v2);
map.put("avg", (v1, v2) -> (v1 * v2) / 2);
Then you can call that map with the operation you want for example :
// an example
String operation = "add";
Double reslt = map.get(operation).applyAsDouble(3, 5);
The simple answer is you cannot assign local variables from upper levels in lambda expressions.
Either, you turn your variable into an instance member, or use an simple if statement:
SomeClass someClass;
switch (type) {
case FIRST:
someClass = new SomeClass();
break;
case SECOND:
OptionalLong optional = findSomeOptional();
if(optional.isPresent()) {
someClass = new SomeClass(optional.getAsLong());
}
}
The last option would be to use an AtomicReference.
Do you have to use an OptionalLong, or can you use an Optional<Long>?
An appropriate idiom for what you want to do is someClass = optional.map(SomeClass::new).orElse(someClass). However, OptionalLong doesn't have a map(LongFunction) method, for some reason.