I am wondering if there is a way to call .apply()
or .get()
on a lambda function directly in the same expression it is defined. The question came to mind when I wanted to initialize a variable which could be private, but I cannot declare it final because the value is the return value of a function that can throw an exception. As an example, consider Files.size(path)
:
final s = Files.size(path);
// code that uses s
Now, if I want to use a default value for s
if there is an exception, I have to add a try/catch
, but this is a statement and not an expression:
s = 0;
try {
s = Files.size();
}
catch (IOException e) {}
// code that uses s
The obvious solution is to just declare a small helper function that wraps the try/catch, e.g. getFileSizeOrZero
. I could also write a generic function that wraps this as a general case. This is nice and descriptive, but a more local function approach whould be better.
In C++, I used lambdas:
const s = []() {
try {
return Files.size(path);
} catch (...) {
return 0;
}
}();
so I wondering if this can be done in Java. The best I could come up with was
final int s = new IntSupplier() {
@Override
public int getAsInt() {
try {
return Files.size(path);
}
catch (IOException e) {
return 0;
}
}
}.getAsInt();
This seems to be quite expressive, so I am hoping that there is some syntac that I don't know enabling
final int s = () -> {
try {
return Files.size(path);
}
catch (IOException e) {
return 0;
}
}.apply();
It seems that the problem is there is no function call operator as in C++, so there has to be a named member function which can be called.
You can do this but you need to explicitly cast the anonymous lambda to the correct type. That's because you need to coerce the type of the lambda to the specific functional interface, Java can't guess the final type, eg:
final int x = ((IntSupplier)() -> 5).getAsInt();
If you don't like it you need to store it in a variable:
final IntSupplier xx = () -> 5;
final int x = xx.getAsInt();
One of your problems is that Files.size(path)
actually returns a long
, so you need to assign it to one.
That allows you to do:
long size = ((LongSupplier) () -> {
try
{
return Files.size(path);
}
catch (IOException e)
{
return 0;
}
}).getAsLong();
So you only have to cast the lambda and have a single logical line expression.
I would also say a general function should be in order
public long defaultOnException(LongSupplier s, long defaultValue) {
try {
return s.getAsLong();
} catch (Exception e) {
return defaultValue;
}
}
which would make your client code
long size = defaultOnException(() -> Files.size(path), 0);
I cannot declare it final because the value is the return value of a function that can throw an exception
Why does this stop you making it final? Just declare the variable outside a try/catch.
final long s;
{
long ss;
try {
ss = Files.size(path);
} catch (IOException e) {
ss = 0;
}
s = ss;
}
Note that you can't simply use s
in place of ss
int the try/catch, because of the rules of definite assignment, specifically:
V is definitely unassigned before a catch block iff all of the following are true:
- V is definitely unassigned after the try block.
- ...
That first condition is not true, so the rest are irrelevant.
There is the alternative:
final int s = (IntSupplier) () -> {
try {
return Files.size(path);
} catch (IOException e) {
return 0;
}
}.getAsInt();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With