Using Unnamed Variables with CompletableFuture

Unnamed variables and patterns can be used when a declaration is required but the value is never used. They are denoted by the underscore character: _.
It was finalized in JDK 22 by JEP 456.
See JEP 456 for the full feature details.

It can be used in many ways.
However one useful place for it is concurrency code with CompletableFuture.
Mainly in scenarios with many asynchronous calls, e.g.:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package dev.artsman.poc.concurrency;

import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;

class Main {
  public static void main(String[] args) {
    var priceFromFuture = CompletableFuture.supplyAsync(findPriceAsync());
    var shippingFromFuture = CompletableFuture.supplyAsync(findShippingAsync());
    var discountFromFuture = CompletableFuture.supplyAsync(findDiscountAsync());
  }

  private static Supplier<Integer> findPriceAsync() {
    return () -> 100;
  }

  private static Supplier<Integer> findShippingAsync() {
    return () -> 30;
  }

  private static Supplier<Integer> findDiscountAsync() {
    return () -> 20;
  }
}

When we need all async calls to complete before working with the data, .allOf comes in handy:

1
2
3
4
5
6
7
8
9
//truncated
class Main {
  public static void main(String[] args) {
    //truncated
    CompletableFuture
      .allOf(priceFromFuture, shippingFromFuture, discountFromFuture);
  }
  //truncated
}

Then, with .thenApply, we can retrieve the values and perform the operation we need:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//truncated
class Main {
  public static void main(String[] args) {
    //truncated
    CompletableFuture
      .allOf(priceFromFuture, shippingFromFuture, discountFromFuture)
      .thenApply(unused -> {
        int price = priceFromFuture.join();
        int shipping = shippingFromFuture.join();
        int discount = discountFromFuture.join();
        return price + shipping - discount;
      });
  }
  //truncated
}

Finally, we use .thenAccept to print the computation result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//truncated
class Main {
  public static void main(String[] args) {
    //truncated
    CompletableFuture
      .allOf(priceFromFuture, shippingFromFuture, discountFromFuture)
      .thenApply(unused -> {
        int price = priceFromFuture.join();
        int shipping = shippingFromFuture.join();
        int discount = discountFromFuture.join();
        return price + shipping - discount;
      })
      .thenAccept(System.out::println)
      .join();
  }
  //truncated
}

This prints the value: 110.

At this point, we can take advantage of unnamed variables:

1
2
3
4
5
6
7
8
9
10
11
12
13
//truncated
class Main {
  public static void main(String[] args) {
    //truncated
      .thenApply(unused -> {
        int price = priceFromFuture.join();
        int shipping = shippingFromFuture.join();
        int discount = discountFromFuture.join();
        return price + shipping - discount;
      });
  }
  //truncated
}

The variable unused has no meaning here because its value is never used.

Since .allOf returns a CompletableFuture<Void>.
The .thenApply parameter is always null.
Being just a required variable that is never used, we can replace it with _:

1
2
3
4
5
6
7
8
9
10
11
12
13
//truncated
class Main {
  public static void main(String[] args) {
    //truncated
      .thenApply(_ -> {
        int price = priceFromFuture.join();
        int shipping = shippingFromFuture.join();
        int discount = discountFromFuture.join();
        return price + shipping - discount;
      });
  }
  //truncated
}

This is where unnamed variables are beautiful: they remove a meaningless name and make the code cleaner.

xoff.