Java 8 Lambda Expressions, Functional Interfaces, and Stream API Tutorial

This article introduces Java 8's lambda expressions and functional interfaces, demonstrates custom functional interfaces, and explains how to use the Stream API with operations such as collect, filter, map, flatMap, max/min, count, reduce, partitioningBy, groupingBy, and joining, providing code examples throughout.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Java 8 Lambda Expressions, Functional Interfaces, and Stream API Tutorial

Java 8 brings lambda expressions, enabling functional programming by passing behavior as immutable values to functions. A functional interface is an interface with a single abstract method, optionally annotated with @FunctionalInterface, and can contain default and static methods.

Example code shows how to use built‑in functional interfaces such as Predicate, Consumer, Function, Supplier, UnaryOperator, and BinaryOperator, as well as a custom interface Worker with a test method.

public class Test {
    public static void main(String[] args) {
        Predicate<Integer> predicate = x -> x > 185;
        Student student = new Student("9龙", 23, 175);
        System.out.println("9龙的身高高于185吗?:" + predicate.test(student.getStature()));

        Consumer<String> consumer = System.out::println;
        consumer.accept("命运由我不由天");

        Function<Student, String> function = Student::getName;
        String name = function.apply(student);
        System.out.println(name);

        Supplier<Integer> supplier = () -> Integer.valueOf(BigDecimal.TEN.toString());
        System.out.println(supplier.get());

        UnaryOperator<Boolean> unaryOperator = uglily -> !uglily;
        Boolean apply2 = unaryOperator.apply(true);
        System.out.println(apply2);

        BinaryOperator<Integer> operator = (x, y) -> x * y;
        Integer integer = operator.apply(2, 3);
        System.out.println(integer);

        test(() -> "我是一个演示的函数式接口");
    }

    /**
     * 演示自定义函数式接口使用
     */
    public static void test(Worker worker) {
        String work = worker.work();
        System.out.println(work);
    }

    public interface Worker {
        String work();
    }
}

The article then moves to the Stream API, explaining lazy (intermediate) and eager (terminal) evaluation. It demonstrates common stream operations:

collect(Collectors.toList()) – converts a stream to a list.

filter – uses a Predicate to keep elements, e.g., students with height < 180.

map – transforms each element, e.g., extracting student names.

flatMap – flattens multiple streams into one.

max / min – finds the maximum or minimum element using a Comparator, returning an Optional.

count – counts elements after a filter.

reduce – aggregates values, illustrated by summing integers.

// collect example
List<Student> studentList = Stream.of(
        new Student("路飞", 22, 175),
        new Student("红发", 40, 180),
        new Student("白胡子", 50, 185)
).collect(Collectors.toList());
System.out.println(studentList);

// filter example
List<Student> list = students.stream()
        .filter(stu -> stu.getStature() < 180)
        .collect(Collectors.toList());
System.out.println(list);

// map example
List<String> names = students.stream()
        .map(Student::getName)
        .collect(Collectors.toList());
System.out.println(names);

// flatMap example
List<Student> studentList = Stream.of(students,
        asList(new Student("艾斯", 25, 183), new Student("雷利", 48, 176)))
        .flatMap(students1 -> students1.stream())
        .collect(Collectors.toList());
System.out.println(studentList);

// max/min example
Optional<Student> max = students.stream()
        .max(Comparator.comparing(stu -> stu.getAge()));
Optional<Student> min = students.stream()
        .min(Comparator.comparing(stu -> stu.getAge()));
if (max.isPresent()) System.out.println(max.get());
if (min.isPresent()) System.out.println(min.get());

// count example
long count = students.stream().filter(s -> s.getAge() < 45).count();
System.out.println("年龄小于45岁的人数是:" + count);

// reduce example
Integer reduce = Stream.of(1, 2, 3, 4).reduce(0, (acc, x) -> acc + x);
System.out.println(reduce);

Advanced collectors are covered, including:

partitioningBy – splits a stream into two groups based on a predicate.

groupingBy – groups elements by a key function, similar to SQL's GROUP BY.

joining – concatenates strings from a stream with optional delimiters, prefix, and suffix.

// partitioningBy example
Map<Boolean, List<Student>> listMap = students.stream()
        .collect(Collectors.partitioningBy(student ->
                student.getSpecialities().contains(SpecialityEnum.SING)));

// groupingBy example
Map<SpecialityEnum, List<Student>> listMap = students.stream()
        .collect(Collectors.groupingBy(student ->
                student.getSpecialities().get(0)));

// joining example
String names = students.stream()
        .map(Student::getName)
        .collect(Collectors.joining(",", "[", "]"));
System.out.println(names);

The article concludes that using Java 8's functional features makes code more expressive and concise, encouraging readers to refactor existing codebases with lambda expressions and stream operations.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaLambdajava8Stream APIFunctional InterfacesCollectors
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.