package org.adoptopenjdk.lambda.tutorial;
/*
* #%L
* lambda-tutorial
* %%
* Copyright (C) 2013 Adopt OpenJDK
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/
import org.adoptopenjdk.lambda.tutorial.exercise1.Color;
import org.adoptopenjdk.lambda.tutorial.exercise1.Shape;
import org.adoptopenjdk.lambda.tutorial.exercise1.Shapes;
import org.adoptopenjdk.lambda.tutorial.util.FeatureMatchers;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static org.adoptopenjdk.lambda.tutorial.exercise1.Color.BLACK;
import static org.adoptopenjdk.lambda.tutorial.exercise1.Color.BLUE;
import static org.adoptopenjdk.lambda.tutorial.exercise1.Color.GREEN;
import static org.adoptopenjdk.lambda.tutorial.exercise1.Color.RED;
import static org.adoptopenjdk.lambda.tutorial.exercise1.Color.YELLOW;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.hasSize;
/**
* Exercise 1 - Internal vs External iteration.
* <p>
* As described in Brian Goetz's State of the Lambda - Libraries Edition[0], Java's collection classes provide a way for
* clients to enumerate the members of a collection. Currently, this is iteration is "External" - that is, the
* collection can be iterated in sequence, by the client code.
* </p>
* <p>
* This refers to the trusty "for loop":
* TODO Bug in the Javdoc processor? It claims the code annotation construct is invalid
* <pre>
* {@code
* for (Shape s: shapes) {
* s.setColor(RED)
* }
* }
* </pre>
* </p>
* <p>
* JDK 8, with lambdas and an updated Collections library, will allow "Internal" iteration. In this case, the collection
* receives some code, and decides how to apply that to its elements. This has several benefits, including:
* - allowing the collection to decide how to handle executing given code, including opening the door to parallelism and laziness
* - leads to a style where operations can be pipelined, into a more fluent, readable style.
* </p>
* <p>
* Internal iteration, using lambda expression syntax, turns the above for loop into:
* <pre>
* {@code
* shapes.forEach(s -> s.setColor(RED))
* }
* </pre>
* Where <code>s -> s.setColor(RED)</code> is a lambda expression, which is passed into forEach, and invoked inside forEach. Lambda
* expressions have a type, called a "Functional Interface". In this case the lambda is of type Consumer. Consumer declares
* a single method, <code>void accept(T t)</code>. This is all hidden by the Java compiler, and unless you extract the lambda
* to a variable, or write a method which accepts a lambda, you don't really need to know about it.
* </p>
* <p>
* The tests below can be made to pass using for loops. Try to make them pass without using a for loop.
* </p>
* [0] http://cr.openjdk.java.net/~briangoetz/lambda/sotc3.html
*
* @see java.lang.Iterable#forEach
* @see Shape
* @see Shapes
* @see Color
*
* @author Graham Allan grundlefleck at gmail dot com
*/
@SuppressWarnings("unchecked")
public class Exercise_1_Test {
/**
* Use forEach to change the color of every shape to RED.
*
* Change the 'method under test' to make the test pass.
*
* @see Shapes#colorAll(java.util.List, Color)
*
* @see Iterable#forEach
* @see Shape#setColor(Color)
*/
@Test
public void changeColorOfAllShapes() {
List<Shape> myShapes = Arrays.asList(new Shape(BLUE), new Shape(BLACK), new Shape(YELLOW));
// method under test
Shapes.colorAll(myShapes, RED);
assertThat(myShapes, hasSize(3));
assertThat(myShapes, everyItem(hasColor(RED)));
}
/**
* Use forEach, with the given StringBuilder, to assemble a String which represents every Shape in the given list.
*
* Change the 'method under test' to make the test pass.
*
* The String should be a concatenation of each Shape's toString.
*
* @see Shapes#makeStringOfAllColors(java.util.List, StringBuilder)
*
* @see Shape#toString()
* @see StringBuilder#append(String)
* @see Iterable#forEach
*/
@Test
public void buildStringRepresentingAllShapes() {
List<Shape> allMyShapes = Arrays.asList(new Shape(RED), new Shape(BLACK), new Shape(YELLOW));
StringBuilder builder = new StringBuilder();
// method under test
Shapes.makeStringOfAllColors(allMyShapes, builder);
assertThat(builder.toString(), equalTo("[a RED shape][a BLACK shape][a YELLOW shape]"));
}
/**
* Use forEach to change the color, and build a string showing the old colors of each shape.
*
* Try to perform both actions using a single call to forEach.
*
* @see Shapes#changeColorAndMakeStringOfOldColors
*
* @see Shape#getColor()
* @see Color#name()
* @see StringBuilder#append(String)
*/
@Test
public void changeColorOfAllShapes_AND_buildStringShowingAllTheOldColors() {
List<Shape> myShapes = Arrays.asList(new Shape(BLUE), new Shape(BLACK), new Shape(YELLOW));
StringBuilder builder = new StringBuilder();
Shapes.changeColorAndMakeStringOfOldColors(myShapes, RED, builder);
assertThat(myShapes, hasSize(3));
assertThat(myShapes, everyItem(hasColor(RED)));
assertThat(builder.toString(), equalTo("[a BLUE shape][a BLACK shape][a YELLOW shape]"));
}
// ----- Test helpers -----
private static Matcher<Shape> hasColor(Color color) {
return FeatureMatchers.from(Matchers.is(color), "has color", "color", Shape::getColor);
}
}