Reputation: 5979
I have a class, in which the static factory method uses computeIfAbsent()
to return an instance:
public class TrafficMonitor {
private static ConcurrentMap<String, TrafficMonitor> instances = new ConcurrentHashMap<>();
private String busNumber;
//private constructor could throw an IOException
private TrafficMonitor(String busNumber) throws IOException {
this.busNumber = busNumber;
// it needs to be in constructor because it call a 3rd party API to instantiate an object which throws IOException
doIoOperation();
}
// static factory method to return a instance of this class
public static TrafficMonitor forBus(String busNumber) throws IOException {
// Compiler error: Unhandled exception: java.io.IOException
return instances.computeIfAbsent(busNumber, TrafficMonitor::new);
}
}
The private constructor throws IOException, in static factory function forBus(String busNumber)
even though I have throws IOException
in signature, compiler still complains that unhandled exception: java.io.IOException
. Why? How to get rid of it?
Another question: in this example, does TrafficMonitor::new
automatically get the argument/parameter busNumber
from the forBus(String busNumber)
function or how does it create the instance with the private constructor which requires an argument String busNumber
?
Upvotes: 2
Views: 491
Reputation: 691715
computeIfAbsent
expects a Function
as argument. The signature of Function's SAM is R apply(T t)
. As you see, a Function
may not throw an IOException
. But your constructor does throw an IOException
. So TrafficMonitor::new
is not a Function. So you can't pass that as argument to computeIfAbsent()
.
Clean way: don't do IO in a constructor:
public class TrafficMonitor {
private static ConcurrentMap<String, TrafficMonitor> instances = new ConcurrentHashMap<>();
private String busNumber;
private boolean initialized;
private TrafficMonitor(String busNumber) {
this.busNumber = busNumber;
}
private synchronized void initialize() throws IOException {
if (!initialized) {
doIoOperation();
initialized = true;
}
}
public static TrafficMonitor forBus(String busNumber) throws IOException {
TrafficMonitor result = instances.computeIfAbsent(busNumber, TrafficMonitor::new);
result.initialize();
return result;
}
}
Less clean way: rethrow IOException as a runtime exception
private TrafficMonitor(String busNumber) {
this.busNumber = busNumber;
try {
doIoOperation();
} catch(IOException e) {
throw UnchedkIOException(e);
}
}
or
public static TrafficMonitor forBus(String busNumber) {
return instances.computeIfAbsent(busNumber, busNumber -> {
try {
return new TrafficMonitor(busNumber);
} catch(IOException e) {
throw UnchedkIOException(e);
}
});
}
Upvotes: 2