diff --git a/core-java-modules/core-java-8-2/pom.xml b/core-java-modules/core-java-8-2/pom.xml
index cc184de529..17f0eea20f 100644
--- a/core-java-modules/core-java-8-2/pom.xml
+++ b/core-java-modules/core-java-8-2/pom.xml
@@ -22,6 +22,7 @@
1.8
1.8
64.2
+ 3.12.2
@@ -30,6 +31,12 @@
icu4j
${icu.version}
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
diff --git a/core-java-modules/core-java-8-2/src/test/java/com/baeldung/bifunction/BiFunctionalInterfacesUnitTest.java b/core-java-modules/core-java-8-2/src/test/java/com/baeldung/bifunction/BiFunctionalInterfacesUnitTest.java
new file mode 100644
index 0000000000..ea63409c88
--- /dev/null
+++ b/core-java-modules/core-java-8-2/src/test/java/com/baeldung/bifunction/BiFunctionalInterfacesUnitTest.java
@@ -0,0 +1,164 @@
+package com.baeldung.bifunction;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BiFunctionalInterfacesUnitTest {
+ @Test
+ public void givenStreamValues_whenMappedToNewValues() {
+ List mapped = Stream.of("hello", "world")
+ .map(word -> word + "!")
+ .collect(Collectors.toList());
+
+ assertThat(mapped).containsExactly("hello!", "world!");
+ }
+
+ @Test
+ public void givenStreamValues_whenReducedWithPrefixingOperation() {
+ String result = Stream.of("hello", "world")
+ .reduce("", (a, b) -> b + "-" + a);
+
+ assertThat(result).isEqualTo("world-hello-");
+ }
+
+ @Test
+ public void givenStreamValues_whenReducedWithPrefixingLambda_thenHasNoTrailingDash() {
+ String result = Stream.of("hello", "world")
+ .reduce("", (a, b) -> combineWithoutTrailingDash(a, b));
+
+ assertThat(result).isEqualTo("world-hello");
+ }
+
+ private String combineWithoutTrailingDash(String a, String b) {
+ if (a.isEmpty()) {
+ return b;
+ }
+ return b + "-" + a;
+ }
+
+ @Test
+ public void givenStreamValues_whenReducedWithPrefixingMethodReference_thenHasNoTrailingDash() {
+ String result = Stream.of("hello", "world")
+ .reduce("", this::combineWithoutTrailingDash);
+
+ assertThat(result).isEqualTo("world-hello");
+ }
+
+ @Test
+ public void givenTwoLists_whenCombined() {
+ List list1 = Arrays.asList("a", "b", "c");
+ List list2 = Arrays.asList(1, 2, 3);
+
+ List result = new ArrayList<>();
+ for (int i=0; i < list1.size(); i++) {
+ result.add(list1.get(i) + list2.get(i));
+ }
+
+ assertThat(result).containsExactly("a1", "b2", "c3");
+ }
+
+ @Test
+ public void givenTwoLists_whenCombinedWithGeneralPurposeCombiner() {
+ List list1 = Arrays.asList("a", "b", "c");
+ List list2 = Arrays.asList(1, 2, 3);
+
+ List result = listCombiner(list1, list2, (a, b) -> a + b);
+
+ assertThat(result).containsExactly("a1", "b2", "c3");
+ }
+
+ private static List listCombiner(List list1,
+ List list2,
+ BiFunction combiner) {
+ List result = new ArrayList<>();
+ for (int i = 0; i < list1.size(); i++) {
+ result.add(combiner.apply(list1.get(i), list2.get(i)));
+ }
+ return result;
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedWithCombiningFunction() {
+ List list1 = Arrays.asList(1.0d, 2.1d, 3.3d);
+ List list2 = Arrays.asList(0.1f, 0.2f, 4f);
+
+ // algorithm to determine if the value in list1 > value in list 2
+ List result = listCombiner(list1, list2, (a, b) -> a > b);
+
+ assertThat(result).containsExactly(true, true, false);
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedWithCombiningFunctionByMethodReference() {
+ List list1 = Arrays.asList(1.0d, 2.1d, 3.3d);
+ List list2 = Arrays.asList(0.1f, 0.2f, 4f);
+
+ // algorithm to determine if the value in list1 > value in list 2
+ List result = listCombiner(list1, list2, this::firstIsGreaterThanSecond);
+
+ assertThat(result).containsExactly(true, true, false);
+ }
+
+ private boolean firstIsGreaterThanSecond(Double a, Float b) {
+ return a > b;
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedForEqualityByCombiningFunction() {
+ List list1 = Arrays.asList(0.1f, 0.2f, 4f);
+ List list2 = Arrays.asList(0.1f, 0.2f, 4f);
+
+ List result = listCombiner(list1, list2, (a, b) -> a.equals(b));
+
+ assertThat(result).containsExactly(true, true, true);
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedForEqualityByCombiningFunctionWithMethodReference() {
+ List list1 = Arrays.asList(0.1f, 0.2f, 4f);
+ List list2 = Arrays.asList(0.1f, 0.2f, 4f);
+
+ List result = listCombiner(list1, list2, Float::equals);
+
+ assertThat(result).containsExactly(true, true, true);
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedWithCombiningFunctionWithCompareTo() {
+ List list1 = Arrays.asList(1.0d, 2.1d, 3.3d);
+ List list2 = Arrays.asList(0.1d, 0.2d, 4d);
+
+ List result = listCombiner(list1, list2, Double::compareTo);
+
+ assertThat(result).containsExactly(1, 1, -1);
+ }
+
+ /**
+ * Allows you to to pass in a lambda or method reference and then
+ * get access to the BiFunction it is meant to become
+ */
+ private static BiFunction asBiFunction(BiFunction function) {
+ return function;
+ }
+
+ @Test
+ public void givenTwoLists_whenComparedWithCombiningFunctionWithComposedBiFunction() {
+ List list1 = Arrays.asList(1.0d, 2.1d, 3.3d);
+ List list2 = Arrays.asList(0.1d, 0.2d, 4d);
+
+ List result = listCombiner(list1, list2,
+ asBiFunction(Double::compareTo)
+ .andThen(i -> i > 0));
+
+ assertThat(result).containsExactly(true, true, false);
+ }
+}