Functional Java Labs

Copyright 2017 - Jacob D. Parr

https://www.jacobparr.com

(all rights reserved)

Functional Java Labs

Lab #01

Duration: 20 minutes

Objective: Test your understanding of how to implement lambdas.

 

Step #1: Create LambdaTest with a main method.


    public class LambdaTest {

	public static void main(String... args) {

            // FILL-IN

	}
    }

Functional Java Labs

Lab #01

Step #2: Create the interface SquarePrinter.


    public interface SquarePrinter {
    	public void printSquareOfA(int a);
    }

Step #3: Back in LambdaTest, create a lambda that squares itself and prints it.

Functional Java Labs

Lab #01

Step #4: Create the interface Squarer.


    public interface Squarer {
    	public int getSquareOfA(int a);
    }

Step #5: Back in LambdaTest, create a lambda that returns the square of itself.

Functional Java Labs

Lab #01

Step #6: Create the interface Multiplier.


    public interface Multiplier {
    	public int getAxB(int a, int b);	
    }

Step #7: Back in LambdaTest, create a lambda that multiplies the two numbers.

Functional Java Labs

Lab #01

Step #8: Create the interface Pi.


    public interface Pi {    
	    public Double getPi();	
    }

Step #9: Back in LambdaTest, create a lambda that
returns value of PI.

Functional Java Labs

Lab #01

Don't continue: Solution follows

Functional Java Labs

Lab #01 - Solutions


        // As a lambda block
        SquarePrinter squarePrinter = x -> {
            System.out.println(x * x);
        };
        squarePrinter.printSquareOfA(3);

        // Could also be a lambda expression
        squarePrinter = x -> System.out.println(x * x);
        squarePrinter.printSquareOfA(5);

        Squarer squarer = x -> x * x;
        int result = squarer.getSquareOfA(9);
        System.out.println("Squarer: " + result);

        Multiplier multiplier = (x, y) -> x * y;
        result = multiplier.getAxB(2,  7);
        System.out.println("Multiplier: " + result);

        Pi pi = () -> 3.14159265359;
        double valueOfPi = pi.getPi();
        System.out.println("PI: "+ valueOfPi);

Functional Java Labs

Lab #02

Duration: 20 minutes

Objective: Test your understanding of how to use the functional interfaces.

 

Step #1: The four interfaces created in Lab #01 are functional interfaces, but only effectively. Update each interface to enforce the requirement of functional interfaces

 

Step #2: Add the static method squareIt(int) to LambdaTest


	private static int squareIt(int x) {
		return x * x;
	}

Functional Java Labs

Lab #02

Step #3: We have three static methods:

  • LambdaTest.squareIt(int)
  • Math.PI
  • java.util.Math.multiplyExact(int, int)

Update LambdaTest, replacing the lambdas in Lab #01 with [static] method references

 

WARNING: There is a gotcha in Step #3

Functional Java Labs

Lab #02

Step #4: Create a fifth interface:


    @FunctionalInterface
    public interface DoubleToBigDecimalConverter {

        BigDecimal convert(double value);

    }

Step #5: Create an instance of DoubleToBigDecimalConverter using a
[constructor] method reference.

 

Step #6: Use our new converter to convert the double result of PI to an instance of BigDecimal.

Functional Java Labs

Lab #02

Don't continue: Solution follows

Functional Java Labs

Lab #02 - Solutions


        SquarePrinter squarePrinter = x -> System.out.println(x * x);
        squarePrinter.printSquareOfA(5);

        Squarer squarer = LambdaTest::squareIt;
        int result = squarer.getSquareOfA(9);
        System.out.println("Squarer: " + result);

        Multiplier multiplier = Math::multiplyExact;
        result = multiplier.getAxB(2,  7);
        System.out.println("Multiplier: " + result);

        Pi pi = () -> Math.PI; // Not a method
        double valueOfPi = pi.getPi();
        System.out.println("PI: "+ valueOfPi);

        DoubleToBigDecimalConverter converter = BigDecimal::new;
        BigDecimal bd = converter.convert(valueOfPi);
        System.out.println("BigDecimal: " + bd);

Functional Java Labs

Lab #03

Duration: 15 minutes

Objective: Test your understanding of how to use default implementations in interfaces.

 

Step #1: Add to the interface Multiplier the method
printAxB(int, int) which multiplies two numbers and then prints the results in the form a * b = c.

 

Step #2: Add to the interface Multiplier the method
square(int) that squares the specified value.

Functional Java Labs

Lab #03

Step #3: Add to the interface Pi the instance method
circumferenceOf(double radius) to compute the circumference of a circle. The formula is C=2πr

 

Step #4: Add to the interface Pi the static method
areaOf(double radius) to compute the area of a circle.
The formula is A=πr^2

Functional Java Labs

Lab #03

Don't continue: Solution follows

Functional Java Labs

Lab #03 - Solutions

@FunctionalInterface
public interface Multiplier {

    int getAxB(int a, int b);

    default void printAxB(int a, int b) {
        System.out.printf("%s * %s = %s", a, b, getAxB(a,b));
    }

    default int square(int n) {
        return getAxB(n, n);
    }
}
@FunctionalInterface
public interface Pi {

    Double getPi();

    default Double circumferenceOf(double radius) {
        return 2 * getPi() * radius;
    }

    static Double areaOf(Pi pi, double radius) {
        return pi.getPi() * radius * radius;
    }
}

Functional Java Labs

Lab #03 - Solutions


        SquarePrinter squarePrinter = x -> System.out.println(x * x);
        squarePrinter.printSquareOfA(5);

        Squarer squarer = LambdaTest::squareIt;
        int result = squarer.getSquareOfA(9);
        System.out.println("Squarer: " + result);

        Multiplier multiplier = Math::multiplyExact;
        result = multiplier.getAxB(2,  7);
        System.out.println("Multiplier: " + result);

        result = multiplier.square(6);
        System.out.println("Multiplier.square: " + result);

        multiplier.printAxB(4, 7);

        Pi pi = () -> Math.PI; // Not a method
        double valueOfPi = pi.getPi();
        System.out.println("PI: "+ valueOfPi);

        DoubleToBigDecimalConverter converter = BigDecimal::new;
        BigDecimal bd = converter.convert(Math.PI);
        System.out.println("BigDecimal: " + bd);

        double area = Pi.areaOf(pi, 4);
        System.out.println("Area: " + area);

        double circumference = pi.circumferenceOf(4);
        System.out.println("Circumference: " + circumference);

Functional Java Labs

Lab #04

Duration: 15 minutes

Objective: Test your understanding of how to use the standard functional interfaces.

 

Step #1: Delete the interface SquarePrinter and replace it with an IntConsumer. Update it to print in the form n^2 = X

 

Step #2: Use the Function interface to double the value N and return the value as the String in the form of n+n = x

 

Step #3: use the BiFunction interface to multiply a float by an integer and return a double.

Functional Java Labs

Lab #04

Don't continue: Solution follows

Functional Java Labs

Lab #04 - Solutions


    IntConsumer squarePrinter = x -> System.out.printf("%s^2 = %s%n", x, x * x);
    squarePrinter.accept(5);

    Function<Integer, String> funcSqr = x -> String.format("%s + %s = %s", x, x, x + x);
    System.out.println("Doubled: " + funcSqr.apply(2));

    BiFunction<Float, Integer, Double> biFunc = (f,i) -> (double)f * i;
    double biResult = biFunc.apply(1.5f, 4);
    System.out.println("Float by Int: " + biResult);

Functional Java Labs

Lab #05

Duration: 45-60 minutes

Objective: Test your understanding of how to aggregate behavior using functional composition

 

Step #1: Create the following class: 

    
    public class Event {
    
        private final String message;
    
        public Event(String message) {
            this.message = message;
        }
    
        public String getMessage() {
            return message;
        }
    }

Functional Java Labs

Lab #05

Step #2: Create the following method:


    public static void handleEvent(Event event, Predicate<Event> eventTester) {
        if (eventTester.test(event)) {
            System.err.println(event.getMessage());
        } else {
            System.out.println(event.getMessage());
        }
    }

Functional Java Labs

Lab #05

Step #3: Create a Predicate<Event> named isError that returns true if the message starts with "ERROR:"


isError.test(new Event("ERROR: We broken"))
should return true.

isError.test(new Event("INFO: It's copacetic"))
should return false. 

Functional Java Labs

Lab #05

Step #4: Create a Predicate<Event> named isWarning that returns true if the message starts with "WARNING:"

 

isWarning.test(new Event("WARNING: Just an FYI"))
should return true.

isWarning.test(new Event("TRACE: Bla bla bla")
should return false. 

Functional Java Labs

Lab #05

Step #5: Composite the two predicates (isError and isWarning) and then call handleEvent(..)

 

The end result being that Events that have a message starting with "ERROR" or "WARNING" should print to System.err

and all other messages should print to System.out.

Functional Java Labs

Lab #05

Step #6: Create a Consumer that accepts a String and named errorPrinter. It should print the specified message to System.err as a new line.

 

Step #7: Create a Consumer that accepts a String and named standardPrinter. It should print the specified message to System.out as a new line.

 

Step #8: Update handleEvent(..) adding the errorPrinter as the third parameter and standardPrinter as the fourth. Replace handleEvent(..)'s calls to System.out and
System.err with the errorPrinter and the standardPrinter.

Functional Java Labs

Lab #05

Step #9: Create a third Consumer called starPrinter that prints 40 asterisks to System.err.

 

Step #10: Without modifying the implementation of any of the other Consumers or the method handleEvent(..) update your code so that errors & warnings are printed with a row of asterisks, the message and then another row of asterisks.

 

Step #11: System.out and System.err get printed to the console. Rerun several times and you will see that the output gets all mixed up. Fix the problem by compositing into the existing Consumers the appropriate System.out.flush() or System.err.flush().

Functional Java Labs

Lab #05

Don't continue: Solution follows

Functional Java Labs

Lab #05 - Solutions


    public static void handleEvent(Event event, 
                                   Predicate<Event> eventTester, 
                                   Consumer<String> errorPrinter, 
                                   Consumer<String> standardPrinter) {

        if (eventTester.test(event)) {
            errorPrinter.accept(event.getMessage());
        } else {
            standardPrinter.accept(event.getMessage());
        }
    }

    public void compositionTests() throws Exception {

        Predicate<Event> isError = (e) -> e.getMessage().startsWith("ERROR");
        Predicate<Event> isWarning = (e) -> e.getMessage().startsWith("WARNING");
        Predicate<Event> isProblematic = isError.or(isWarning);

        Consumer<String> errorPrinter = System.err::println;
        Consumer<String> starPrinter = (x) -> System.err.println("***************************************");
        Consumer<String> compositeErrPrinter = starPrinter
            .andThen(errorPrinter)
            .andThen(starPrinter)
            .andThen( (msg) -> System.err.flush());

        Consumer<String> standardPrinter = System.out::println;
        Consumer<String> compositeOutPrinter = standardPrinter
            .andThen( (msg) -> System.out.flush() );

        handleEvent(new Event("ERROR: We broken"), isProblematic, compositeErrPrinter, compositeOutPrinter);
        handleEvent(new Event("INFO: It's copacetic"), isProblematic, compositeErrPrinter, compositeOutPrinter);
        handleEvent(new Event("WARNING: Just an FYI"), isProblematic, compositeErrPrinter, compositeOutPrinter);
        handleEvent(new Event("TRACE: Bla bla bla"), isProblematic, compositeErrPrinter, compositeOutPrinter);
    }

Functional Java Labs

Lab #06

Duration: 20 minutes

Objective: Test your understanding of how to use streams

 

Step #1: Iterate through the number 1 & 100 inclusive and print each number.

 

Step #2: Modify the algorithm to print only odd numbers.

 

Step #3: Modify the algorithm to sum all the odd numbers.

 

Step #4: Use "option #2" to produce the sum. 

Advance to the next slide to reveal the hint to step #1.

Functional Java Labs

Lab #06

Step #1 Hint

 

Start with IntStream()

 

Conclude with forEach(..)

Advance to the next slide to reveal the hint to step #2.

Functional Java Labs

Lab #06

Step #2 Hint

 

Add the intermediate operation filter(..) 

Advance to the next slide to reveal the hint to step #3 & #4.

Functional Java Labs

Lab #06

Step #3 & #4 Hint

 

One solution is to producing the sum using reduce(..)

 

The second solution uses a convenience method of IntStream

Functional Java Labs

Lab #06

Don't continue: Solution follows

Functional Java Labs

Lab #06 - Solutions


        IntStream.rangeClosed(1, 100)
            .forEach(System.out::println);


        IntStream.rangeClosed(1, 100)
            .filter( i -> i % 2 != 0)
            .forEach(System.out::println);


        int sum = IntStream.rangeClosed(1, 100)
            .filter( i -> i % 2 != 0)
            .reduce(0, (l,r) -> l + r );
        System.out.println("Sum #1: " + sum);


        sum = IntStream.rangeClosed(1, 100)
            .filter( i -> i % 2 != 0)
            .sum();
        System.out.println("Sum #2: " + sum);

Functional Java Labs

Lab #07

Duration: < 5 minutes

Objective: Prove you are qualified for the job

 

Step #1: Implement the Fizz Buzz algorithm using the streams API. By definition, it doesn't really matter how you solve the problem, just that you do. 

 

Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz".

Functional Java Labs

Lab #07

Don't continue: Solution follows

Functional Java Labs

Lab #07 - Solution


    IntStream.rangeClosed(1, 100)
        .forEach( x -> {
            if (x % 3 == 0 && x % 5 == 0) {
                System.out.format("%s FizzBuzz%n", x);
            } else if (x % 3 == 0) {
                System.out.format("%s Fizz%n", x);
            } else if (x % 5 == 0) {
                System.out.format("%s Buzz%n", x);
            } else {
                System.out.println(x);
            }
        });

Functional Java Labs

Lab #08

Duration: < 5 minutes

Objective: Prove you are qualified for the job

 

Step #2: Re-implement  the algorithm without an if statement

 

Hint: A good data structure makes a world of difference!


    class Entry {
        final int n;
        String s;
        public Entry(Integer n) {
            this.n = n;
            this.s = "";
        }
        public Entry update(String s) {
            this.s += s;
            return this;
        }
    }

Functional Java Labs

Lab #08

Don't continue: Solution follows

Functional Java Labs

Lab #08 - Solution


    Stream
        .iterate(1, i -> ++i)
        .limit(100)
        .map(Entry::new)
        .map( e -> e.update(e.n % 3 == 0 ? "Fizz" : ""))
        .map( e -> e.update(e.n % 5 == 0 ? "Buzz" : ""))
        .map( e -> e.s.length() == 0 ? e.n : e.s)
        .forEach(System.out::println);


    class Entry {
        final int n;
        String s;
        public Entry(Integer n) {
            this.n = n;
            this.s = "";
        }
        public Entry update(String s) {
            this.s += s;
            return this;
        }
    }

Functional Java Labs

Lab #09

Duration: < 5 minutes

Objective: Prove you are qualified for the job

 

Step #2: Re-implement  the algorithm without an if statement

and use an array (or arrays) as temporary storage.

Functional Java Labs

Lab #09

Don't continue: Solution follows

Functional Java Labs

Lab #09 - Solution


    IntStream
        .rangeClosed(1, 100)
        .mapToObj( i -> new Object[]{ i, (i % 3 == 0 ? "Fizz" : "") + " " + (i % 5 == 0 ? "Buzz" : "") } )
        .map( a -> a[1].toString().length() == 1 ? a[0] : a[1].toString().trim())
        .forEach(System.out::println);

Function Java Labs

By Jacob D Parr

Function Java Labs

  • 919