Kotlinlearncs.online LogoJava

    ← Prev

    Index

    Next →

    Kotlin
    Java
    • Algorithms and Lists : 44

    • Lambda Expressions : 43

    • Anonymous Classes : 42

    • Practice with Interfaces : 41

    • Implementing Interfaces : 40

    • Using Interfaces : 39

    • Working with Exceptions : 38

    • Throwing Exceptions : 37

    • Catching Exceptions : 36

    • References and Polymorphism : 35

    • References : 34

    • Data Modeling 2 : 33

    • Equality and Object Copying : 32

    • Polymorphism : 31

    • Inheritance : 30

    • Data Modeling 1 : 29

    • Static : 28

    • Encapsulation : 27

    • Constructors : 26

    • Objects, Continued : 25

    • Introduction to Objects : 24

    • Compilation and Type Inference : 23

    • Practice with Collections : 22

    • Maps and Sets : 21

    • Lists and Type Parameters : 20

    • Imports and Libraries : 19

    • Multidimensional Arrays : 18

    • Practice with Strings : 17

    • null : 16

    • Algorithms and Strings : 15

    • Strings : 14

    • Functions and Algorithms : 13

    • Practice with Functions : 12

    • More About Functions : 11

    • Errors and Debugging : 10

    • Functions : 9

    • Practice with Loops and Algorithms : 8

    • Algorithms I : 7

    • Loops : 6

    • Arrays : 5

    • Compound Conditionals : 4

    • Conditional Expressions and Statements : 3

    • Operations on Variables : 2

    • Variables and Types : 1

    • Hello, world! : 0

    Lambda Expressions

    interface Modify {
    int modify(int value);
    }
    Modify first = (value) -> value + 1;
    Modify second = (value) -> value - 10;
    System.out.println(first.modify(10));
    System.out.println(second.modify(3));

    We saw how anonymous classes could encapsulate reusable logic and allow us to build, for example, a general purpose counting method. But their syntax still left a lot to be desired! In this lesson we’ll improve on that by introducing lambda expressions. Awesome! Let’s start.

    As a reminder, this is an advanced topic. We’re introducing it because you will see lambda expressions in real Java code, particularly when working with user interfaces.

    Warm Up
    Warm Up

    But let’s warm up with a classic practice problem on software testing! This is similar to the problem you’ll need to solve for this lesson’s homework.

    Created By: Geoffrey Challen
    / Version: 2020.10.0

    Create a public class TestArraySum that provides a single void class method named test. test accepts a single parameter: an instance of ArraySum.

    Each ArraySum provides a method sum that accepts an int[] and returns the sum of the values as an int, or 0 if the array is null. However, some ArraySum implementations are broken! Your job is to identify all of them correctly.

    To do this you should use assert to test various inputs. Here's an example:

    Your function does not need to return a value. Instead, if the code is correct no assertion should fail, and if it is incorrect one should.

    As you design test inputs, here are two conflicting objectives to keep in mind:

    • Less is more. The fewer inputs you need to identify all broken cases, the more readable your test suite will be.
    • Think defensively. At the same time, you want to anticipate all the different mistakes that a programmer might make. You've probably made many of these yourself! Examples include forgetting to check for null, off-by-one errors, not handling special cases properly, etc.

    Good luck and have fun!

    Functional Programming
    Functional Programming

    Java is an object-oriented programming language. With the exception of the primitive types, everything in Java is an object, and so Java programs involve manipulating objects.

    However, other programming languages introduce different programming styles. One powerful and interesting style of programming is known as functional programming:

    In computer science, functional programming is a programming paradigm where programs are constructed by applying and composing functions.

    Let’s examine the Wikipedia definition together and contrast it with Java’s object-oriented style:

    First-Class Functions
    First-Class Functions

    One characteristic of true functional programming languages is that functions (or methods) are first-class citizens. They can be stored in variables and passed to and returned from other functions, just like any other kind of data.

    Java does not support first-class functions. Only object references and primitive type values can be stored in variables and passed to and returned from functions.

    Getting to Lambda Expressions
    Getting to Lambda Expressions

    However, it turns out that we can achieve something very similar to first-class functions in Java! It looks like this:

    interface Modify {
    int modify(int value);
    }
    // first stores a reference to a function that returns its argument increased by one
    Modify first = (value) -> value + 1;
    System.out.println(first.modify(7));

    We accomplish this by combining two things we already know—interfaces and anonymous classes—with some new Java syntax. Let’s see how, step by step.

    But first, let’s state our goal.

    Functional Interfaces
    Functional Interfaces

    Our first ingredient is called a functional interface. A functional interface is any old Java interface, but with one restriction: it can only provide one method. We’ll see why in a minute.

    Other than that, there are no restrictions on what a functional interface looks like. Here’s one:

    interface Modify {
    int modify(int value);
    }

    Here’s another:

    interface Filter {
    boolean accept(int first, int second);
    }

    Anonymous Classes
    Anonymous Classes

    Next, we need a way to create something that implements a functional interface on the fly. But wait—we already know how to do that! It’s called an anonymous object:

    interface Filter {
    boolean accept(int first, int second);
    }
    Filter bothBositive = new Filter() {
    @Override
    public boolean accept(int first, int second) {
    return first > 0 && second > 0;
    }
    };
    Filter bothNegative = new Filter() {
    @Override
    public boolean accept(int first, int second) {
    return first < 0 && second < 0;
    }
    };

    Lambda Expressions
    Lambda Expressions

    We are so close now. Imagine that we want to save into a variable a method that increments an int by one. Here’s what it looks like given what we already know. First we need our functional interface, and then an anonymous class to implement it correctly:

    interface Modify {
    int modify(int value);
    }
    Modify first = new Modify() {
    @Override
    public int modify(int value) {
    return value + 1;
    }
    };
    System.out.println(first.modify(7));

    But we can do better! Let’s see how:

    interface Modify {
    int modify(int value);
    }
    Modify first = new Modify() {
    @Override
    public int modify(int value) {
    return value + 1;
    }
    };
    System.out.println(first.modify(7));

    Practice: Adder with Lambda

    Created By: Geoffrey Challen
    / Version: 2020.10.0

    Declare a public class Modifier providing one static method adder. adder takes a single int parameter and returns a method that implements the Modify functional interface:

    The returned "function" should implement modify so that it adds the value passed to adder. So, for example:

    The correct solution to this problem is a single line lambda expression!

    Array Counting with Lambdas
    Array Counting with Lambdas

    To finish up, let’s return to our example from last time that used anonymous classes to count arrays in different ways. We’ll reimplement it using lambdas and show how much cleaner and more direct this syntax is.

    // Array Counting with Lambdas

    Homework: Test Array Max

    Created By: Geoffrey Challen
    / Version: 2021.10.0

    Create a public class TestArrayMax that provides a single void class method named test. test accepts a single parameter: an instance of ArrayMax.

    Each ArrayMax provides a method max that accepts an int[] and returns the maximum of the values as an int. If the array is null or empty max should throw an IllegalArgumentException.

    However, some ArrayMax implementations are broken! Your job is to identify all of them correctly. To do this you should use assert to test various inputs. (Do not throw other kinds of exceptions on failure.)

    Here's an example:

    Your function does not need to return a value. Instead, if the code is correct no assertion should fail, and if it is incorrect one should.

    As you design test inputs, here are two conflicting objectives to keep in mind:

    • Less is more. The fewer inputs you need to identify all broken cases, the more readable your test suite will be.
    • Think defensively. At the same time, you want to anticipate all the different mistakes that a programmer might make. You've probably made many of these yourself! Examples include forgetting to check for null, off-by-one errors, not handling special cases properly, etc.

    You'll also need to think about how to use try-catch to handle places where the code should throw an exception, how to ensure that it does, and how to make sure it throws the right type.

    Good luck and have fun!

    More Practice

    Need more practice? Head over to the practice page.