Lambda Expressions and Functional Interfaces practice questions

From OCP Java SE 25 (1Z0-831) · 15 questions on this topic

Lambda Expressions and Functional Interfaces practice questions from OCP Java SE 25 (1Z0-831). This pack has 15 questions tagged Lambda Expressions and Functional Interfaces, drawn from its timed mock exams. 8 of them are worked through in full below — the question, every option, why each is right or wrong, and the explanation.

Worked examples for Lambda Expressions and Functional Interfaces

  1. Question 1

    What does this print? ```java import java.util.function.*; public class Main { public static void main(String[] args) { Consumer<StringBuilder> addX = sb -> sb.append("x"); Consumer<StringBuilder> addY = sb -> sb.append("y"); StringBuilder box = new StringBuilder(); addX.andThen(addY).accept(box); System.out.println(box); } } ```

    1. A. xyCorrect answer

      Correct. andThen runs the receiver (append "x") first and then the after-consumer (append "y") on the same StringBuilder, so it holds "xy" (Consumer.andThen).

    2. B. yx

      Reverses the order: andThen runs the receiver first, not the after-consumer first, so "yx" is wrong.

    3. C. x

      Assumes only the receiver runs; andThen guarantees the after-consumer runs too, so the "y" is not skipped.

    4. D. y

      Assumes only the after-consumer runs; the receiver is not skipped, so the "x" is still appended.

    Explanation

    Consumer.andThen returns a new Consumer that runs the receiver first and then the supplied after-consumer, both acting on the same argument. Because a Consumer returns nothing, no value passes between the two stages; the side effects simply apply left to right to the one shared StringBuilder.

  2. Question 2

    This program builds predicates with the static factories Predicate.not and Predicate.isEqual. What does it print? ```java import java.util.function.*; public class Main { public static void main(String[] args) { Predicate<String> blank = String::isBlank; Predicate<String> filled = Predicate.not(blank); Predicate<String> isJava = Predicate.isEqual("java"); System.out.println(filled.test(" ") + " " + filled.test("x") + " " + isJava.test(new String("java"))); } } ```

    1. A. true true true

      Confuses isBlank with isEmpty; " ".isEmpty() is false (length 1), so treating " " as filled is wrong — " ".isBlank() is true, so its negation makes the first result false.

    2. B. true false true

      Reads Predicate.not backwards, treating the negated predicate as if it still meant 'is blank', which inverts both of the first two results.

    3. C. false true trueCorrect answer

      String::isBlank tests the argument, and " " is blank so its Predicate.not negation gives false while "x" gives true; Predicate.isEqual uses Objects.equals (value equality), so it returns true for new String("java") despite the distinct instance — false true true.

    4. D. false true false

      Believes Predicate.isEqual compares with ==; it is defined in terms of Objects.equals, so new String("java") still tests true — only a hand-written s -> s == "java" would give false there.

    Explanation

    Trace: `String::isBlank` is an unbound-receiver reference matching `Predicate<String>` — the tested string becomes the receiver. `" ".isBlank()` is `true` (isBlank asks whether the string is empty *or contains only white space*), so `filled`, which is its negation via `Predicate.not`, gives `false` for `" "`. `"x"` is not blank, so `filled.test("x")` is `true`. `Predicate.isEqual(target)` returns a predicate that tests with `Objects.equals(target, arg)` — value equality, not reference identity — so it returns `true` for `new String("java")` even though that object is a distinct instance from the interned literal. Output: `false true true`. Why the others are wrong: `true true true` confuses `isBlank` with `isEmpty`. `" ".isEmpty()` is `false` (length 1), and a student who substitutes that reading makes `" "` count as filled. `true false true` reads `Predicate.not` backwards, treating `filled` as if it still meant "is blank" — it inverts both of the first two results. `false true false` encodes the belief that `Predicate.isEqual` compares with `==`. It does not; only a predicate you write yourself as `s -> s == "java"` would print `false` there, because `new String("java")` deliberately dodges the string pool. Exam tip: memorise the two static factories on `Predicate` — `not(p)` (added in Java 11, the readable form of `p.negate()`) and `isEqual(target)` (`Objects.equals`-based, and null-safe in both directions). The reverse trap is `Predicate.isEqual(null)`, which is a *valid* predicate that returns true only for a null argument.

  3. Question 3

    Two method references to methods of Integer are assigned to Function<Integer, String>. What is the result of compiling and running this program? ```java import java.util.function.Function; public class Main { public static void main(String[] args) { Function<Integer, String> hex = Integer::toHexString; Function<Integer, String> plain = Integer::toString; System.out.println(hex.apply(255) + " " + plain.apply(7)); } } ```

    1. A. ff 7

      Assumes both references are valid; the first resolves fine, but the toString reference is ambiguous, and one bad reference fails the whole compilation.

    2. B. 255 7

      Assumes the toString reference is the instance no-arg form on the argument; it matches both a static and an instance method and is rejected as ambiguous.

    3. C. Compilation failsCorrect answer

      The toString reference matches both the static form taking an int and the unbound instance form, so it is an ambiguous method reference and the program fails to compile.

    4. D. 0xff 7

      Assumes hex output is prefixed; toHexString produces no prefix, but the compile error means nothing is printed anyway.

    Explanation

    A Type::method reference is checked for both a static form matching the descriptor and an unbound-instance form where the first parameter becomes the receiver. When both forms are applicable the reference is ambiguous and rejected at compile time. A single unresolvable reference fails the entire compilation, even though another reference in the same file is valid.

  4. Question 4

    The nested interface below is annotated @FunctionalInterface and declares an abstract method, an equals declaration, a default method and a static factory. What is the result of compiling and running this program? ```java public class Main { @FunctionalInterface interface Calc { int apply(int a, int b); boolean equals(Object other); default int twice(int n) { return apply(n, n); } static Calc sum() { return Integer::sum; } } public static void main(String[] args) { Calc c = (a, b) -> a * b; System.out.println(Calc.sum().apply(2, 3) + " " + c.twice(4)); } } ```

    1. A. Compilation fails

      Assumes the extra equals, default, and static methods break the functional interface; only one abstract method that is not an Object method counts, and there is exactly one.

    2. B. 5 16Correct answer

      The redeclared equals is a public Object method and does not count, so the single abstract method is the calculation; the static factory sums 2 and 3 to 5, and the multiplying lambda's default twice applies the abstract method to 4 and 4, giving 16.

    3. C. 5 8

      Misreads the default twice as two times its argument; it is defined as applying the abstract method to the argument twice, which for the multiplying lambda is 16.

    4. D. 6 16

      Applies the multiplying lambda instead of the summing factory result; the static factory returns a summing implementation, so its result on 2 and 3 is 5.

    Explanation

    A functional interface may have exactly one abstract method that does not override a public method of Object; a redeclared equals does not count, and default and static methods never count. The static factory produces a summing implementation, while the lambda bound to the variable multiplies, and the inherited default method delegates to whichever abstract implementation the instance holds.

  5. Question 5

    What is the result of compiling and running this code? ```java import java.util.function.*; public class Main { public static void main(String[] args) { int value = 5; Function<Integer,Integer> f = value -> value + 1; System.out.println(f.apply(value)); } } ```

    1. A. It prints 6

      Assumes the code compiles; it does not, so nothing is printed. (6 is what the call would yield if the parameter were renamed.)

    2. B. It prints 11

      Also assumes it compiles, and mis-adds besides; the code does not compile.

    3. C. Compilation fails: a lambda parameter cannot reuse the name of an enclosing local variableCorrect answer

      Correct. A lambda parameter may not redeclare a local variable whose scope includes the lambda, so javac reports that the name is already defined and compilation fails.

    4. D. Compilation fails: the captured variable value is not effectively final

      The local is effectively final (never reassigned); the error is the duplicate parameter name, not a capture violation.

    Explanation

    Unlike a nested class, a lambda does not open a new scope for its parameter names; they share the scope of the enclosing method. Declaring a lambda parameter with the same name as an in-scope local variable is therefore a duplicate declaration, so javac rejects it.

  6. Question 6

    What does this print? ```java import java.util.function.*; public class Main { public static void main(String[] args) { IntPredicate even = n -> n % 2 == 0; IntPredicate over10 = n -> n > 10; System.out.println(even.and(over10).test(12) + " " + even.negate().test(6)); } } ```

    1. A. false false

      Assumes the and() fails; 12 satisfies both being even and being over 10, so the combined result is true.

    2. B. true true

      Assumes negate() leaves an even number true; negate() flips it to false.

    3. C. false true

      Inverts both results; the and() is true and the negate() is false.

    4. D. true falseCorrect answer

      Correct. even.and(over10).test(12) is true (12 is even and over 10); even.negate().test(6) is false (6 is even, inverted). Output is true false.

    Explanation

    and() combines two predicates so the result is true only when both hold, and 12 is both even and greater than 10. negate() returns a new predicate that inverts the original, so testing an even number through negate() yields false while leaving the original predicate unchanged.

  7. Question 7

    What does this print? ```java import java.util.function.*; public class Main { public static void main(String[] args) { BiFunction<String,String,Boolean> eq = String::equals; System.out.println(eq.apply("gray", "grey") + " " + eq.apply("gray", "gray")); } } ```

    1. A. true false

      Reverses both results: "gray" and "grey" differ (false), and two identical strings are equal (true).

    2. B. Compilation fails: String::equals is not a BiFunction

      An unbound reference to a one-parameter instance method has an effective arity of two once the receiver is counted, so it matches BiFunction and compiles.

    3. C. false false

      Ignores that the second call compares two identical strings, which is true.

    4. D. false trueCorrect answer

      Correct. String::equals is unbound, so apply(x,y) means x.equals(y): "gray".equals("grey") is false and "gray".equals("gray") is true, giving false true.

    Explanation

    An unbound instance-method reference has an effective arity of the receiver plus the declared parameters of the method, so String::equals fits a two-argument BiFunction with the first argument acting as the receiver. Each call therefore evaluates x.equals(y), returning false for two different strings and true for two identical ones.

  8. Question 8

    A bound method reference and a lambda are both created from the same static field, and then the field is reassigned. What does this print? ```java import java.util.function.*; public class Main { static StringBuilder buf = new StringBuilder("A"); public static void main(String[] args) { Supplier<String> bound = buf::toString; Supplier<String> lazy = () -> buf.toString(); buf = new StringBuilder("B"); System.out.println(bound.get() + lazy.get()); } } ```

    1. A. Compilation fails: buf must be final or effectively final to be used in a lambda

      Applies the effectively-final rule to the wrong thing; that rule constrains only local variables and parameters, and buf is a static field that may be reassigned freely, so both forms compile.

    2. B. AA

      Assumes the lambda also snapshots the field at creation; a lambda body closes over local-variable values, never fields, so the field access is a live read and yields B, not A.

    3. C. BB

      Assumes buf::toString re-reads the field on every call like the lambda; a bound method reference evaluates its receiver eagerly at creation, capturing the StringBuilder holding A.

    4. D. ABCorrect answer

      Correct: the bound reference captured the StringBuilder holding A when it was created, so bound.get() is A, while the lambda re-reads buf (now pointing at B) on each call, giving AB.

    Explanation

    Trace: in a *bound* method reference `expr::method`, the receiver expression is evaluated once, eagerly, when the method reference itself is evaluated — not on each call. So `buf::toString` reads `buf` at that moment and captures the `StringBuilder` holding `"A"`; the later `buf = new StringBuilder("B")` rebinds the field but cannot reach into the already-captured reference, so `bound.get()` still returns `A`. The lambda `() -> buf.toString()` captures nothing: its body re-reads the static field every time it runs, and by then the field points at `"B"`, so `lazy.get()` returns `B`. Concatenated, the program prints `AB`. Why the others are wrong: `BB` encodes the belief that `buf::toString` is just shorthand for `() -> buf.toString()` and re-reads the field on every call. The two forms differ precisely in *when* the receiver is evaluated. `AA` encodes the mirror-image belief — that the lambda also snapshots the field at creation. Lambda bodies close over *values of local variables*, never over fields; a field access inside a lambda body is a live read of the field through the captured `this` (or of the class, when it is static). `Compilation fails: buf must be final or effectively final...` applies the effectively-final rule to the wrong thing. That rule constrains only *local variables and parameters*; `buf` is a static field, so it may be reassigned freely and both forms compile. Exam tip: `expr::method` evaluates `expr` now; `() -> expr.method()` evaluates it later. The exam signals this by mutating something between the capture and the call. Note the related distinction: had the code kept the same object and merely *mutated* it (`buf.append("!")`), the bound reference would see the change — it captured the reference, not a copy of the contents.

Practise all 15 Lambda Expressions and Functional Interfaces questions

OCP Java SE 25 has the full set, inside timed mock exams that mirror real exam conditions — every question with a worked explanation.

Open OCP Java SE 25

Other topics in this pack