Download a PDF of this article
More quiz questions available here
Imagine that you are developing a high-load message board application that will use nonblocking communication with a database. This communication will be handled using the following service class:
public class MsgBoardService {
public int getCommentCount(int threadId) throws SQLException {
... // implementation logic here
}
... // more similar methods
}
Which functional interface could be used to expose the MsgBoardService getCommentCount
method? Choose one.
A. | Function |
|
B. | Supplier |
|
C. | IntUnaryOperator |
|
D. | Callable |
|
E. | A custom interface |
Answer. You have a method that takes a single argument of int
type and returns an int
. Let’s walk through each of the proposed functional interfaces to see how well they match up with these types.
The Function<A,R>
interface declares a method that returns an object of type R
and takes a single argument of type A
. Of course, these generic arguments must be of reference type, but primitive values can be handled using autoboxing if you are willing to sacrifice a little efficiency.
The Supplier<R>
interface declares a method that returns an object of type R
but takes no arguments. That’s not a great fit for tasks. It is possible to write a function factory that would embed the necessary argument into a closure and then have that new function delegate to the getCommentCount
method. This, however, is clearly a rather circuitous route and can’t really be considered a proper fit.
The IntUnaryOperator
interface declares a method that returns a primitive int
and accepts a primitive int
as its argument. This looks promising as IntUnaryOperator
is a perfect fit in terms of the argument and return types, but let’s not jump to conclusions.
The Callable<R>
interface declares a method that takes no arguments and returns an object of type R
. At this level of investigation, this appears to be the same situation as with Supplier
—but why would two identical interfaces exist? Well, of course, they’re not identical. The Callable call()
method is declared with throws Exception
, whereas the Supplier
, in keeping with all the interfaces of the java.util.function
package, does not declare any throws
clause.
This distinction highlights the observation that the getCommentCount
method is declared as throws SQLException
, which is a checked exception and, thus, regardless of the argument and return types, the Function
, Supplier
, and IntUnaryOperator
interfaces are all unsuited to this question’s task because they cannot support the checked exception. Since Callable
is also unsuited because it cannot take an argument, you must conclude that options A, B, C, and D are incorrect and that a custom interface is necessary. Therefore, option E is correct.
The custom interface could look like this.
public interface ExceptionIntUnaryOperator {
int apply(int i) throws Exception;
}
There are some side notes worth discussing that arise from this question. Perhaps the most compelling is, “Since so many of Java’s core libraries report problems with checked exceptions, why do the standard functional interfaces in java.util.function
not declare any checked exceptions?” There are two levels of answer.
map
method of a stream, the entire stream processing method will crash. That’s not a failure of the stream implementation. If you think about it, there’s no clean way to embed exception handling (which by nature must be specific to the application domain) into the general stream code.The only use for exceptions is to represent catastrophic errors either in the design or the implementation of the code. Such errors should result in a system shutting down, not in a recovery attempt. This is simply because if the system has done something that by the intended design should be impossible, it’s impossible to know how messed up everything might be, and you have no solid ground from which to attempt a recovery.
This observation then raises the question of how functional-style software should represent a recoverable error (such as a database being offline, which merely requires the plug to be reconnected to permit a retry). Generally, the returned value of a function that might fail will be an object (or tuple) that contains one of two values: It must either return the successful result or provide the reason for failure. Such a class is often called an Either
; such a class exists in the Vavr functional library, for example.
If you don’t care about the reason for a failure, and the only reason for the absence of a result is failure, the Optional
class in the core Java APIs could be used instead. In that case, you could put this system together using IntFunction<R>
, which declares a method that takes a primitive int
and returns an object of type R
. The result could look like this.
IntFunction<OptionalInt> msgCommentCounter = threadId -> {
try {
return OptionalInt.of(getCommentCount(threadId));
} catch(SQLException sqle) {
return OptionalInt.empty();
}
};
Conclusion. The correct answer is option E.
Mikalai Zaikin is a lead Java developer at IBA Lithuania (part of worldwide IBA Group) and currently located in Vilnius. During his career, Zaikin has helped Oracle with development of Java certification exams, and he has been a technical reviewer of several Java certification books, including three editions of the famous Sun Certified Programmer for Java study guides by Kathy Sierra and Bert Bates.
Simon Roberts joined Sun Microsystems in time to teach Sun’s first Java classes in the UK. He created the Sun Certified Java Programmer and Sun Certified Java Developer exams. He wrote several Java certification guides and is currently a freelance educator who publishes recorded and live video training through Pearson InformIT (available direct and through the O’Reilly Safari Books Online service). He remains involved with Oracle’s Java certification projects.
Next Post