From f781ed164c1edff9ac2594efa117ebdeb29d5e0e Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Thu, 11 Apr 2013 21:13:31 +0100
Subject: [PATCH 01/61] Make tests pass with a prejava8 (imperative) solution.
---
.../lambda/tutorial/exercise1/Shapes.java | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
index 6978e32..f49873b 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
@@ -13,14 +13,21 @@
public class Shapes {
public static void colorAll(List shapes, Color newColor) {
- // [your code here]
+ for (Shape s: shapes) {
+ s.setColor(newColor);
+ }
}
public static void makeStringOfAllColors(List shapes, StringBuilder stringBuilder) {
- // [your code here]
+ for (Shape s: shapes) {
+ stringBuilder.append(s);
+ }
}
public static void changeColorAndMakeStringOfOldColors(List shapes, Color newColor, StringBuilder stringBuilder) {
- // [your code here]
+ for (Shape s: shapes) {
+ stringBuilder.append("[" + s.getColor() + "]");
+ s.setColor(newColor);
+ }
}
}
From 874e2ed27aabe22e7ab41dc5e1262d1952b2ac01 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Thu, 11 Apr 2013 21:13:31 +0100
Subject: [PATCH 02/61] Make tests pass with a prejava8 (imperative) solution.
---
.../lambda/tutorial/exercise1/Shapes.java | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
index 6978e32..f49873b 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
@@ -13,14 +13,21 @@
public class Shapes {
public static void colorAll(List shapes, Color newColor) {
- // [your code here]
+ for (Shape s: shapes) {
+ s.setColor(newColor);
+ }
}
public static void makeStringOfAllColors(List shapes, StringBuilder stringBuilder) {
- // [your code here]
+ for (Shape s: shapes) {
+ stringBuilder.append(s);
+ }
}
public static void changeColorAndMakeStringOfOldColors(List shapes, Color newColor, StringBuilder stringBuilder) {
- // [your code here]
+ for (Shape s: shapes) {
+ stringBuilder.append("[" + s.getColor() + "]");
+ s.setColor(newColor);
+ }
}
}
From 8fd74c9da77a7b5e88e3efa1e53f8eea78c1f3e3 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Thu, 11 Apr 2013 23:16:53 +0100
Subject: [PATCH 03/61] Add post jdk8 solution for first test of exercise 2.
---
.../adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java
index e8fe55a..658ebd9 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java
@@ -8,9 +8,9 @@
public class VotingRules {
public static List eligibleVoters(List potentialVoters, int legalAgeOfVoting) {
- // [your code here]
-
- return Collections.emptyList();
+ return potentialVoters.stream()
+ .filter(p -> p.age >= legalAgeOfVoting)
+ .collect(Collectors.toList());
}
}
From 8200a87592086f98dfff2b8452ec0f8e76b04a64 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Thu, 11 Apr 2013 23:25:07 +0100
Subject: [PATCH 04/61] Add a pre jdk 8 solution.
---
.../lambda/tutorial/exercise2/VotingRules.java | 14 +++++++-------
.../lambda/tutorial/Exercise_2_Test.java | 10 +++++-----
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java
index e8fe55a..2981a5a 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java
@@ -1,16 +1,16 @@
package org.adoptopenjdk.lambda.tutorial.exercise2;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
import java.util.List;
-import java.util.stream.Collectors;
public class VotingRules {
public static List eligibleVoters(List potentialVoters, int legalAgeOfVoting) {
- // [your code here]
-
-
- return Collections.emptyList();
+ List eligible = new ArrayList<>();
+ for (Person p: potentialVoters) {
+ if (p.age >= legalAgeOfVoting) {
+ eligible.add(p);
+ }
+ }
+ return eligible;
}
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index 341a461..b08f6d0 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -16,6 +16,7 @@
import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.hasSize;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -106,11 +107,10 @@ public class Exercise_2_Test {
*/
@Test
public void getAllPersonsEligibleToVote() {
- Person tom = new Person("Tom", 24);
- Person dick = new Person("Dick", 75);
- Person harry = new Person("Harry", 17);
-
- List potentialVoters = Arrays.asList(tom, dick, harry);
+ List potentialVoters = new ArrayList<>();
+ potentialVoters.add(new Person("Tom", 24));
+ potentialVoters.add(new Person("Dick", 75));
+ potentialVoters.add(new Person("Harry", 17));
int legalAgeOfVoting = 18;
List eligibleVoters = VotingRules.eligibleVoters(potentialVoters, legalAgeOfVoting);
From 3c5bb2ab24c7b20141434e353804cd543ba1035c Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sat, 13 Apr 2013 00:11:32 +0100
Subject: [PATCH 05/61] Add a second example for the filtering & collecting
test.
---
.../tutorial/exercise2/ElectoralDistrict.java | 33 ++++++++
.../tutorial/exercise2/RegisteredVoter.java | 31 ++++++++
.../lambda/tutorial/Exercise_2_Test.java | 76 ++++++++++++++-----
3 files changed, 123 insertions(+), 17 deletions(-)
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
new file mode 100644
index 0000000..2df69ec
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
@@ -0,0 +1,33 @@
+package org.adoptopenjdk.lambda.tutorial.exercise2;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Lambda Tutorial -- Adopt Open JDK
+ *
+ * @author Graham Allan grundlefleck at gmail dot com
+ */
+public enum ElectoralDistrict {
+
+ // Some (inaccurate) London electrical districts
+ CROYDON("CR"),
+ BARKING("BA"),
+ HACKNEY("HA"),
+ EDMONTON("ED");
+
+ // ... ~ 650 more for the UK
+
+ public final String prefix;
+
+ ElectoralDistrict(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public static Set votersIn(ElectoralDistrict district, Collection voters) {
+ // [your code here]
+
+ return Collections.emptySet();
+ }
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
new file mode 100644
index 0000000..f22904b
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
@@ -0,0 +1,31 @@
+package org.adoptopenjdk.lambda.tutorial.exercise2;
+
+/**
+ * Lambda Tutorial -- Adopt Open JDK
+ *
+ * @author Graham Allan grundlefleck at gmail dot com
+ */
+public final class RegisteredVoter {
+ public final String electorId;
+
+ public RegisteredVoter(String electorId) {
+ this.electorId = electorId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RegisteredVoter that = (RegisteredVoter) o;
+
+ if (!electorId.equals(that.electorId)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return electorId.hashCode();
+ }
+}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index 341a461..a946b88 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -1,29 +1,29 @@
package org.adoptopenjdk.lambda.tutorial;
-import org.adoptopenjdk.lambda.tutorial.exercise1.Color;
-import org.adoptopenjdk.lambda.tutorial.exercise1.Shape;
+import org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict;
import org.adoptopenjdk.lambda.tutorial.exercise2.Person;
+import org.adoptopenjdk.lambda.tutorial.exercise2.RegisteredVoter;
import org.adoptopenjdk.lambda.tutorial.exercise2.VotingRules;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.junit.Test;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.everyItem;
-import static org.hamcrest.Matchers.hasSize;
-
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static java.util.Arrays.asList;
+import static org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict.HACKNEY;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.hasSize;
+
/**
* Exercise 2 - Filtering and Collecting
*
@@ -100,17 +100,14 @@
public class Exercise_2_Test {
/**
- * Use List.filter() to produce a list containing only those Persons eligible to vote.
- *
+ * Use Stream.filter() to produce a list containing only those Persons eligible to vote.
*
+ * @see Person#age
*/
@Test
public void getAllPersonsEligibleToVote() {
- Person tom = new Person("Tom", 24);
- Person dick = new Person("Dick", 75);
- Person harry = new Person("Harry", 17);
-
- List potentialVoters = Arrays.asList(tom, dick, harry);
+ List potentialVoters =
+ new ArrayList<>(asList(new Person("Tom", 24), new Person("Dick", 75), new Person("Harry", 17)));
int legalAgeOfVoting = 18;
List eligibleVoters = VotingRules.eligibleVoters(potentialVoters, legalAgeOfVoting);
@@ -123,6 +120,43 @@ public void getAllPersonsEligibleToVote() {
assertThat(potentialVoters, containsInAnyOrder(aPersonNamed("Tom"), aPersonNamed("Dick"), aPersonNamed("Harry")));
}
+ /**
+ * Uses Stream.filter() to find all the voters residing in a given district.
+ *
+ * The resulting collection is to be used for quick lookups to find if a given
+ * voter resides in a district. Performance measurements indicate we should
+ * prefer the result to be a java.util.Set. Use Stream.collect() to produce a
+ * Set containing the result, rather than a List.
+ *
+ * HINT: sometimes type inference is "not there yet", help out the compiler
+ * with explicit generic arguments if you have to.
+ *
+ * @see Stream#collect(java.util.stream.Collector)
+ * @see java.util.stream.Collectors#toSet()
+ *
+ * @see ElectoralDistrict#prefix
+ * @see RegisteredVoter#electorId
+ */
+ @Test public void setOfVotersInDistrict() {
+ List allVoters = new ArrayList<>(asList(
+ new RegisteredVoter("CR2345"),
+ new RegisteredVoter("HA7654"),
+ new RegisteredVoter("HA2213"),
+ new RegisteredVoter("BA9987"),
+ new RegisteredVoter("CR6203"),
+ new RegisteredVoter("ED9876")
+ // ... and many more
+ ));
+
+ Set votersInHackney = ElectoralDistrict.votersIn(HACKNEY, allVoters);
+
+ assertThat(votersInHackney, hasSize(2));
+ assertThat(votersInHackney, containsInAnyOrder(aVoterWithId("HA7654"), aVoterWithId("HA2213")));
+ }
+
+
+ // Test helpers
+
private static Matcher aPersonNamed(String name) {
return new FeatureMatcher(Matchers.is(name), "is a person", "name") {
@Override protected String featureValueOf(Person person) {
@@ -130,4 +164,12 @@ private static Matcher aPersonNamed(String name) {
}
};
}
+
+ private static Matcher aVoterWithId(String name) {
+ return new FeatureMatcher(Matchers.is(name), "is a voter", "electorId") {
+ @Override protected String featureValueOf(RegisteredVoter voter) {
+ return voter.electorId;
+ }
+ };
+ }
}
From d75a0c0f87808c2c50cdaef53a3e86398b5f1b6c Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sat, 13 Apr 2013 00:30:06 +0100
Subject: [PATCH 06/61] Reinstate accidentally removed jdk8 solution for
exercise 1.
---
.../org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
index 6978e32..1839713 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
@@ -13,14 +13,15 @@
public class Shapes {
public static void colorAll(List shapes, Color newColor) {
- // [your code here]
+ shapes.forEach(s -> s.setColor(newColor));
}
public static void makeStringOfAllColors(List shapes, StringBuilder stringBuilder) {
- // [your code here]
+ shapes.forEach(s -> stringBuilder.append(s));
}
public static void changeColorAndMakeStringOfOldColors(List shapes, Color newColor, StringBuilder stringBuilder) {
- // [your code here]
+ shapes.forEach(s -> { stringBuilder.append("[" + s.getColor() + "]"); s.setColor(newColor); });
+
}
}
From 2b22244515ef34d954bff9754955782764c1c882 Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sat, 13 Apr 2013 00:31:33 +0100
Subject: [PATCH 07/61] Add a jdk 8 solution for the second test.
---
.../lambda/tutorial/exercise2/ElectoralDistrict.java | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
index 2df69ec..975a221 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
@@ -3,6 +3,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Lambda Tutorial -- Adopt Open JDK
@@ -26,8 +27,8 @@ public enum ElectoralDistrict {
}
public static Set votersIn(ElectoralDistrict district, Collection voters) {
- // [your code here]
-
- return Collections.emptySet();
+ return voters.stream()
+ .filter(v -> v.electorId.startsWith(district.prefix))
+ .collect(Collectors.toSet());
}
}
From a8dc95d4056b66cfbf4f3ae94fff0e236d67a960 Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sat, 13 Apr 2013 00:11:32 +0100
Subject: [PATCH 08/61] Merge latest test from master.
---
.../tutorial/exercise2/ElectoralDistrict.java | 33 +++++++++
.../tutorial/exercise2/RegisteredVoter.java | 31 ++++++++
.../lambda/tutorial/Exercise_2_Test.java | 74 +++++++++++++++----
3 files changed, 122 insertions(+), 16 deletions(-)
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
new file mode 100644
index 0000000..2df69ec
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
@@ -0,0 +1,33 @@
+package org.adoptopenjdk.lambda.tutorial.exercise2;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Lambda Tutorial -- Adopt Open JDK
+ *
+ * @author Graham Allan grundlefleck at gmail dot com
+ */
+public enum ElectoralDistrict {
+
+ // Some (inaccurate) London electrical districts
+ CROYDON("CR"),
+ BARKING("BA"),
+ HACKNEY("HA"),
+ EDMONTON("ED");
+
+ // ... ~ 650 more for the UK
+
+ public final String prefix;
+
+ ElectoralDistrict(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public static Set votersIn(ElectoralDistrict district, Collection voters) {
+ // [your code here]
+
+ return Collections.emptySet();
+ }
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
new file mode 100644
index 0000000..f22904b
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
@@ -0,0 +1,31 @@
+package org.adoptopenjdk.lambda.tutorial.exercise2;
+
+/**
+ * Lambda Tutorial -- Adopt Open JDK
+ *
+ * @author Graham Allan grundlefleck at gmail dot com
+ */
+public final class RegisteredVoter {
+ public final String electorId;
+
+ public RegisteredVoter(String electorId) {
+ this.electorId = electorId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RegisteredVoter that = (RegisteredVoter) o;
+
+ if (!electorId.equals(that.electorId)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return electorId.hashCode();
+ }
+}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index b08f6d0..a946b88 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -1,30 +1,29 @@
package org.adoptopenjdk.lambda.tutorial;
-import org.adoptopenjdk.lambda.tutorial.exercise1.Color;
-import org.adoptopenjdk.lambda.tutorial.exercise1.Shape;
+import org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict;
import org.adoptopenjdk.lambda.tutorial.exercise2.Person;
+import org.adoptopenjdk.lambda.tutorial.exercise2.RegisteredVoter;
import org.adoptopenjdk.lambda.tutorial.exercise2.VotingRules;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.junit.Test;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.everyItem;
-import static org.hamcrest.Matchers.hasSize;
-
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static java.util.Arrays.asList;
+import static org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict.HACKNEY;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.hasSize;
+
/**
* Exercise 2 - Filtering and Collecting
*
@@ -101,16 +100,14 @@
public class Exercise_2_Test {
/**
- * Use List.filter() to produce a list containing only those Persons eligible to vote.
- *
+ * Use Stream.filter() to produce a list containing only those Persons eligible to vote.
*
+ * @see Person#age
*/
@Test
public void getAllPersonsEligibleToVote() {
- List potentialVoters = new ArrayList<>();
- potentialVoters.add(new Person("Tom", 24));
- potentialVoters.add(new Person("Dick", 75));
- potentialVoters.add(new Person("Harry", 17));
+ List potentialVoters =
+ new ArrayList<>(asList(new Person("Tom", 24), new Person("Dick", 75), new Person("Harry", 17)));
int legalAgeOfVoting = 18;
List eligibleVoters = VotingRules.eligibleVoters(potentialVoters, legalAgeOfVoting);
@@ -123,6 +120,43 @@ public void getAllPersonsEligibleToVote() {
assertThat(potentialVoters, containsInAnyOrder(aPersonNamed("Tom"), aPersonNamed("Dick"), aPersonNamed("Harry")));
}
+ /**
+ * Uses Stream.filter() to find all the voters residing in a given district.
+ *
+ * The resulting collection is to be used for quick lookups to find if a given
+ * voter resides in a district. Performance measurements indicate we should
+ * prefer the result to be a java.util.Set. Use Stream.collect() to produce a
+ * Set containing the result, rather than a List.
+ *
+ * HINT: sometimes type inference is "not there yet", help out the compiler
+ * with explicit generic arguments if you have to.
+ *
+ * @see Stream#collect(java.util.stream.Collector)
+ * @see java.util.stream.Collectors#toSet()
+ *
+ * @see ElectoralDistrict#prefix
+ * @see RegisteredVoter#electorId
+ */
+ @Test public void setOfVotersInDistrict() {
+ List allVoters = new ArrayList<>(asList(
+ new RegisteredVoter("CR2345"),
+ new RegisteredVoter("HA7654"),
+ new RegisteredVoter("HA2213"),
+ new RegisteredVoter("BA9987"),
+ new RegisteredVoter("CR6203"),
+ new RegisteredVoter("ED9876")
+ // ... and many more
+ ));
+
+ Set votersInHackney = ElectoralDistrict.votersIn(HACKNEY, allVoters);
+
+ assertThat(votersInHackney, hasSize(2));
+ assertThat(votersInHackney, containsInAnyOrder(aVoterWithId("HA7654"), aVoterWithId("HA2213")));
+ }
+
+
+ // Test helpers
+
private static Matcher aPersonNamed(String name) {
return new FeatureMatcher(Matchers.is(name), "is a person", "name") {
@Override protected String featureValueOf(Person person) {
@@ -130,4 +164,12 @@ private static Matcher aPersonNamed(String name) {
}
};
}
+
+ private static Matcher aVoterWithId(String name) {
+ return new FeatureMatcher(Matchers.is(name), "is a voter", "electorId") {
+ @Override protected String featureValueOf(RegisteredVoter voter) {
+ return voter.electorId;
+ }
+ };
+ }
}
From c40f4167a1eb5003c274cdbe2c0db2beec4f4a5c Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sat, 13 Apr 2013 00:36:30 +0100
Subject: [PATCH 09/61] Add pre jdk8 solution for exercise 2 test 2.
---
.../lambda/tutorial/exercise2/ElectoralDistrict.java | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
index 2df69ec..91dd030 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
@@ -2,6 +2,7 @@
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Set;
/**
@@ -26,8 +27,13 @@ public enum ElectoralDistrict {
}
public static Set votersIn(ElectoralDistrict district, Collection voters) {
- // [your code here]
-
- return Collections.emptySet();
+ Set votersInDistrict = new HashSet<>();
+ for (RegisteredVoter v: voters) {
+ if (v.electorId.startsWith(district.prefix)) {
+ votersInDistrict.add(v);
+ }
+ }
+
+ return votersInDistrict;
}
}
From 54f216142fdbb4f454b9aa56d8a798caf3bbd91f Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sun, 21 Apr 2013 12:37:57 +0100
Subject: [PATCH 10/61] Add another filter example.
---
.../lambda/tutorial/exercise2/Ballot.java | 32 ++++++++
.../tutorial/exercise2/ElectoralDistrict.java | 25 +++++-
.../lambda/tutorial/exercise2/Party.java | 14 ++++
.../tutorial/exercise2/RegisteredVoter.java | 7 ++
.../lambda/tutorial/Exercise_2_Test.java | 81 +++++++++++++++++--
5 files changed, 148 insertions(+), 11 deletions(-)
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Ballot.java
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Party.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Ballot.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Ballot.java
new file mode 100644
index 0000000..71f31be
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Ballot.java
@@ -0,0 +1,32 @@
+package org.adoptopenjdk.lambda.tutorial.exercise2;
+
+/**
+ * Lambda Tutorial -- Adopt Open JDK
+ *
+ * @author Graham Allan grundlefleck at gmail dot com
+ */
+public final class Ballot {
+ public final boolean isSpoiled;
+ public final Party party;
+
+ private Ballot(Party party) {
+ this.party = party;
+ this.isSpoiled = party == null;
+ }
+
+ @Override
+ public String toString() {
+ return "Ballot{" +
+ "isSpoiled=" + isSpoiled +
+ ", party=" + party +
+ '}';
+ }
+
+ public static Ballot voteFor(Party party) {
+ return new Ballot(party);
+ }
+
+ public static Ballot spoiled() {
+ return new Ballot(null);
+ }
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
index 975a221..94e182a 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
@@ -1,9 +1,21 @@
package org.adoptopenjdk.lambda.tutorial.exercise2;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
import java.util.stream.Collectors;
+import java.util.stream.DelegatingStream;
+import java.util.stream.Stream;
/**
* Lambda Tutorial -- Adopt Open JDK
@@ -27,8 +39,15 @@ public enum ElectoralDistrict {
}
public static Set votersIn(ElectoralDistrict district, Collection voters) {
- return voters.stream()
- .filter(v -> v.electorId.startsWith(district.prefix))
- .collect(Collectors.toSet());
+ // [your code here]
+
+ return Collections.emptySet();
+ }
+
+ public static Set unspoiledBallots(Set votes) {
+ // [your code here]
+
+ return Collections.emptySet();
}
}
+
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Party.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Party.java
new file mode 100644
index 0000000..6e4a63d
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Party.java
@@ -0,0 +1,14 @@
+package org.adoptopenjdk.lambda.tutorial.exercise2;
+
+/**
+ * Lambda Tutorial -- Adopt Open JDK
+ *
+ * @author Graham Allan grundlefleck at gmail dot com
+ */
+public enum Party {
+ CONSERVATIVE,
+ LABOUR,
+ LIBERAL_DEMOCRATS,
+ GREEN_PARTY,
+ MONSTER_RAVING_LOONY_PARTY
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
index f22904b..b2ef97d 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
@@ -28,4 +28,11 @@ public boolean equals(Object o) {
public int hashCode() {
return electorId.hashCode();
}
+
+ @Override
+ public String toString() {
+ return "RegisteredVoter{" +
+ "electorId='" + electorId + '\'' +
+ '}';
+ }
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index a946b88..9ab5fae 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -1,6 +1,8 @@
package org.adoptopenjdk.lambda.tutorial;
+import org.adoptopenjdk.lambda.tutorial.exercise2.Ballot;
import org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict;
+import org.adoptopenjdk.lambda.tutorial.exercise2.Party;
import org.adoptopenjdk.lambda.tutorial.exercise2.Person;
import org.adoptopenjdk.lambda.tutorial.exercise2.RegisteredVoter;
import org.adoptopenjdk.lambda.tutorial.exercise2.VotingRules;
@@ -11,18 +13,28 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
+import static java.util.Arrays.binarySearch;
import static org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict.HACKNEY;
+import static org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict.votersIn;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
/**
* Exercise 2 - Filtering and Collecting
@@ -154,22 +166,75 @@ public void getAllPersonsEligibleToVote() {
assertThat(votersInHackney, containsInAnyOrder(aVoterWithId("HA7654"), aVoterWithId("HA2213")));
}
+ @Test public void removeAllSpoiledBallots() {
+ Set votes = new HashSet<>(asList(
+ Ballot.voteFor(Party.LABOUR),
+ Ballot.voteFor(Party.CONSERVATIVE),
+ Ballot.spoiled(),
+ Ballot.voteFor(Party.MONSTER_RAVING_LOONY_PARTY),
+ Ballot.voteFor(Party.LIBERAL_DEMOCRATS),
+ Ballot.spoiled(),
+ Ballot.voteFor(Party.GREEN_PARTY),
+ Ballot.voteFor(Party.GREEN_PARTY)
+ // ... and many more
+ ));
+
+ Set unspoiledBallots = ElectoralDistrict.unspoiledBallots(votes);
+
+ assertThat(unspoiledBallots, hasSize(6));
+ assertThat(unspoiledBallots, everyItem(is(not(spoiled()))));
+ }
+
+ /**
+ * Ensure that the Set returned cannot be modified by callers by wrapping the result
+ * in Collections.unmodifiableSet().
+ *
+ * The Streams API does not provide a way to wrap the final result, as one of its operations. So just wrap the
+ * result in an unmodifiableSet yourself. Sometimes it's just as important to know what an API _doesn't_ do.
+ *
+ * @see Stream#collect(java.util.stream.Collector)
+ * @see java.util.stream.Collectors#toSet()
+ * @see Collections#unmodifiableSet(java.util.Set)
+ */
+ @Test public void setOfVotersInDistrictInUnmodifiableSet() throws ClassNotFoundException {
+ List allVoters = new ArrayList<>(asList(
+ new RegisteredVoter("CR2345"),
+ new RegisteredVoter("HA7654"),
+ new RegisteredVoter("HA2213"),
+ new RegisteredVoter("BA9987"),
+ new RegisteredVoter("CR6203"),
+ new RegisteredVoter("ED9876")
+ // ... and many more
+ ));
+
+ Set votersInHackney = ElectoralDistrict.votersIn(HACKNEY, allVoters);
+
+ assertThat(votersInHackney, instanceOf(Class.forName("java.util.Collections$UnmodifiableSet")));
+ }
// Test helpers
private static Matcher aPersonNamed(String name) {
- return new FeatureMatcher(Matchers.is(name), "is a person", "name") {
- @Override protected String featureValueOf(Person person) {
- return person.name;
- }
- };
+ return featureMatcher(is(name), "a person", "name", p -> p.name);
}
private static Matcher aVoterWithId(String name) {
- return new FeatureMatcher(Matchers.is(name), "is a voter", "electorId") {
- @Override protected String featureValueOf(RegisteredVoter voter) {
- return voter.electorId;
+ return featureMatcher(is(name), "a voter", "electorId", v -> v.electorId);
+ }
+
+ private static Matcher spoiled() {
+ return featureMatcher(equalTo(true), "a spoiled ballot", "isSpoiled", b -> b.isSpoiled);
+ }
+
+ private static Matcher featureMatcher(Matcher featureMatcher,
+ String description,
+ String name,
+ Function extractor) {
+ return new FeatureMatcher(featureMatcher, description, name) {
+ @Override protected FEATURE featureValueOf(FROM t) {
+ return extractor.apply(t);
}
};
}
+
}
From e774fbe08158de005bbc855aa1bcd113bf73a3aa Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sun, 21 Apr 2013 12:37:57 +0100
Subject: [PATCH 11/61] Add another filter example.
---
.../lambda/tutorial/exercise2/Ballot.java | 32 ++++++++
.../tutorial/exercise2/ElectoralDistrict.java | 19 +++++
.../lambda/tutorial/exercise2/Party.java | 14 ++++
.../tutorial/exercise2/RegisteredVoter.java | 7 ++
.../lambda/tutorial/Exercise_2_Test.java | 81 +++++++++++++++++--
5 files changed, 145 insertions(+), 8 deletions(-)
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Ballot.java
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Party.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Ballot.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Ballot.java
new file mode 100644
index 0000000..71f31be
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Ballot.java
@@ -0,0 +1,32 @@
+package org.adoptopenjdk.lambda.tutorial.exercise2;
+
+/**
+ * Lambda Tutorial -- Adopt Open JDK
+ *
+ * @author Graham Allan grundlefleck at gmail dot com
+ */
+public final class Ballot {
+ public final boolean isSpoiled;
+ public final Party party;
+
+ private Ballot(Party party) {
+ this.party = party;
+ this.isSpoiled = party == null;
+ }
+
+ @Override
+ public String toString() {
+ return "Ballot{" +
+ "isSpoiled=" + isSpoiled +
+ ", party=" + party +
+ '}';
+ }
+
+ public static Ballot voteFor(Party party) {
+ return new Ballot(party);
+ }
+
+ public static Ballot spoiled() {
+ return new Ballot(null);
+ }
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
index 91dd030..91d551e 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
@@ -1,9 +1,21 @@
package org.adoptopenjdk.lambda.tutorial.exercise2;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.DelegatingStream;
+import java.util.stream.Stream;
/**
* Lambda Tutorial -- Adopt Open JDK
@@ -36,4 +48,11 @@ public static Set votersIn(ElectoralDistrict district, Collecti
return votersInDistrict;
}
+
+ public static Set unspoiledBallots(Set votes) {
+ // [your code here]
+
+ return Collections.emptySet();
+ }
}
+
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Party.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Party.java
new file mode 100644
index 0000000..6e4a63d
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/Party.java
@@ -0,0 +1,14 @@
+package org.adoptopenjdk.lambda.tutorial.exercise2;
+
+/**
+ * Lambda Tutorial -- Adopt Open JDK
+ *
+ * @author Graham Allan grundlefleck at gmail dot com
+ */
+public enum Party {
+ CONSERVATIVE,
+ LABOUR,
+ LIBERAL_DEMOCRATS,
+ GREEN_PARTY,
+ MONSTER_RAVING_LOONY_PARTY
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
index f22904b..b2ef97d 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/RegisteredVoter.java
@@ -28,4 +28,11 @@ public boolean equals(Object o) {
public int hashCode() {
return electorId.hashCode();
}
+
+ @Override
+ public String toString() {
+ return "RegisteredVoter{" +
+ "electorId='" + electorId + '\'' +
+ '}';
+ }
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index a946b88..9ab5fae 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -1,6 +1,8 @@
package org.adoptopenjdk.lambda.tutorial;
+import org.adoptopenjdk.lambda.tutorial.exercise2.Ballot;
import org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict;
+import org.adoptopenjdk.lambda.tutorial.exercise2.Party;
import org.adoptopenjdk.lambda.tutorial.exercise2.Person;
import org.adoptopenjdk.lambda.tutorial.exercise2.RegisteredVoter;
import org.adoptopenjdk.lambda.tutorial.exercise2.VotingRules;
@@ -11,18 +13,28 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
+import static java.util.Arrays.binarySearch;
import static org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict.HACKNEY;
+import static org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict.votersIn;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
/**
* Exercise 2 - Filtering and Collecting
@@ -154,22 +166,75 @@ public void getAllPersonsEligibleToVote() {
assertThat(votersInHackney, containsInAnyOrder(aVoterWithId("HA7654"), aVoterWithId("HA2213")));
}
+ @Test public void removeAllSpoiledBallots() {
+ Set votes = new HashSet<>(asList(
+ Ballot.voteFor(Party.LABOUR),
+ Ballot.voteFor(Party.CONSERVATIVE),
+ Ballot.spoiled(),
+ Ballot.voteFor(Party.MONSTER_RAVING_LOONY_PARTY),
+ Ballot.voteFor(Party.LIBERAL_DEMOCRATS),
+ Ballot.spoiled(),
+ Ballot.voteFor(Party.GREEN_PARTY),
+ Ballot.voteFor(Party.GREEN_PARTY)
+ // ... and many more
+ ));
+
+ Set unspoiledBallots = ElectoralDistrict.unspoiledBallots(votes);
+
+ assertThat(unspoiledBallots, hasSize(6));
+ assertThat(unspoiledBallots, everyItem(is(not(spoiled()))));
+ }
+
+ /**
+ * Ensure that the Set returned cannot be modified by callers by wrapping the result
+ * in Collections.unmodifiableSet().
+ *
+ * The Streams API does not provide a way to wrap the final result, as one of its operations. So just wrap the
+ * result in an unmodifiableSet yourself. Sometimes it's just as important to know what an API _doesn't_ do.
+ *
+ * @see Stream#collect(java.util.stream.Collector)
+ * @see java.util.stream.Collectors#toSet()
+ * @see Collections#unmodifiableSet(java.util.Set)
+ */
+ @Test public void setOfVotersInDistrictInUnmodifiableSet() throws ClassNotFoundException {
+ List allVoters = new ArrayList<>(asList(
+ new RegisteredVoter("CR2345"),
+ new RegisteredVoter("HA7654"),
+ new RegisteredVoter("HA2213"),
+ new RegisteredVoter("BA9987"),
+ new RegisteredVoter("CR6203"),
+ new RegisteredVoter("ED9876")
+ // ... and many more
+ ));
+
+ Set votersInHackney = ElectoralDistrict.votersIn(HACKNEY, allVoters);
+
+ assertThat(votersInHackney, instanceOf(Class.forName("java.util.Collections$UnmodifiableSet")));
+ }
// Test helpers
private static Matcher aPersonNamed(String name) {
- return new FeatureMatcher(Matchers.is(name), "is a person", "name") {
- @Override protected String featureValueOf(Person person) {
- return person.name;
- }
- };
+ return featureMatcher(is(name), "a person", "name", p -> p.name);
}
private static Matcher aVoterWithId(String name) {
- return new FeatureMatcher(Matchers.is(name), "is a voter", "electorId") {
- @Override protected String featureValueOf(RegisteredVoter voter) {
- return voter.electorId;
+ return featureMatcher(is(name), "a voter", "electorId", v -> v.electorId);
+ }
+
+ private static Matcher spoiled() {
+ return featureMatcher(equalTo(true), "a spoiled ballot", "isSpoiled", b -> b.isSpoiled);
+ }
+
+ private static Matcher featureMatcher(Matcher featureMatcher,
+ String description,
+ String name,
+ Function extractor) {
+ return new FeatureMatcher(featureMatcher, description, name) {
+ @Override protected FEATURE featureValueOf(FROM t) {
+ return extractor.apply(t);
}
};
}
+
}
From f3eb2e29c3753c86f4e554611c5ad199d8cfc89c Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 23 Apr 2013 21:48:12 +0100
Subject: [PATCH 12/61] clarify what I'm talking about when I say inference 'is
no there yet'
---
.../java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index 9ab5fae..e2b854d 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -140,7 +140,7 @@ public void getAllPersonsEligibleToVote() {
* prefer the result to be a java.util.Set. Use Stream.collect() to produce a
* Set containing the result, rather than a List.
*
- * HINT: sometimes type inference is "not there yet", help out the compiler
+ * HINT: sometimes type inference is "not there yet", in either the IDE or javac, help out the compiler
* with explicit generic arguments if you have to.
*
* @see Stream#collect(java.util.stream.Collector)
From c74dbbb113fd6fda28c35b40839477583afe4877 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 23 Apr 2013 21:48:12 +0100
Subject: [PATCH 13/61] clarify what I'm talking about when I say inference 'is
no there yet'
---
.../java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index 9ab5fae..e2b854d 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -140,7 +140,7 @@ public void getAllPersonsEligibleToVote() {
* prefer the result to be a java.util.Set. Use Stream.collect() to produce a
* Set containing the result, rather than a List.
*
- * HINT: sometimes type inference is "not there yet", help out the compiler
+ * HINT: sometimes type inference is "not there yet", in either the IDE or javac, help out the compiler
* with explicit generic arguments if you have to.
*
* @see Stream#collect(java.util.stream.Collector)
From ef2bca3b73f6f641e3b0ac917428c0c42341a6cf Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 23 Apr 2013 21:59:19 +0100
Subject: [PATCH 14/61] Add pre java 8 solution for filtering unspoiled
ballots.
---
.../tutorial/exercise2/ElectoralDistrict.java | 23 +++++++------------
1 file changed, 8 insertions(+), 15 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
index 91d551e..bbb3e48 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
@@ -1,21 +1,9 @@
package org.adoptopenjdk.lambda.tutorial.exercise2;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.BinaryOperator;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collector;
-import java.util.stream.Collectors;
-import java.util.stream.DelegatingStream;
-import java.util.stream.Stream;
/**
* Lambda Tutorial -- Adopt Open JDK
@@ -46,13 +34,18 @@ public static Set votersIn(ElectoralDistrict district, Collecti
}
}
- return votersInDistrict;
+ return Collections.unmodifiableSet(votersInDistrict);
}
public static Set unspoiledBallots(Set votes) {
- // [your code here]
+ Set unspoiledBallots = new HashSet<>();
+ for (Ballot v: votes) {
+ if (!v.isSpoiled) {
+ unspoiledBallots.add(v);
+ }
+ }
- return Collections.emptySet();
+ return unspoiledBallots;
}
}
From 026679959209c4aafb8a6bbf702f6a30d44f9308 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 23 Apr 2013 22:04:42 +0100
Subject: [PATCH 15/61] Add lambda solutions for exercise 2.
---
.../tutorial/exercise2/ElectoralDistrict.java | 23 ++++++-------------
1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
index 94e182a..39559bf 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
@@ -1,21 +1,9 @@
package org.adoptopenjdk.lambda.tutorial.exercise2;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.BinaryOperator;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collector;
import java.util.stream.Collectors;
-import java.util.stream.DelegatingStream;
-import java.util.stream.Stream;
/**
* Lambda Tutorial -- Adopt Open JDK
@@ -39,15 +27,18 @@ public enum ElectoralDistrict {
}
public static Set votersIn(ElectoralDistrict district, Collection voters) {
- // [your code here]
+ Set fromDistrict = voters.stream()
+ .filter(v -> v.electorId.startsWith(district.prefix))
+ .collect(Collectors.toSet());
- return Collections.emptySet();
+ return Collections.unmodifiableSet(fromDistrict);
}
public static Set unspoiledBallots(Set votes) {
- // [your code here]
+ return votes.stream()
+ .filter(v -> !v.isSpoiled)
+ .collect(Collectors.toSet());
- return Collections.emptySet();
}
}
From c8f424c23e759139b287cebf93e6fac0c28eba5e Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 23 Apr 2013 22:08:28 +0100
Subject: [PATCH 16/61] Add javadoc for the final unit test for filtering.
---
.../org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index e2b854d..8a12668 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -133,7 +133,7 @@ public void getAllPersonsEligibleToVote() {
}
/**
- * Uses Stream.filter() to find all the voters residing in a given district.
+ * Use Stream.filter() to find all the voters residing in a given district.
*
* The resulting collection is to be used for quick lookups to find if a given
* voter resides in a district. Performance measurements indicate we should
@@ -166,6 +166,12 @@ public void getAllPersonsEligibleToVote() {
assertThat(votersInHackney, containsInAnyOrder(aVoterWithId("HA7654"), aVoterWithId("HA2213")));
}
+ /**
+ * Use Stream.filter() to remove all the ballots that have been spoiled.
+ *
+ * @see ElectoralDistrict#unspoiledBallots(Set)
+ * @see Ballot#isSpoiled
+ */
@Test public void removeAllSpoiledBallots() {
Set votes = new HashSet<>(asList(
Ballot.voteFor(Party.LABOUR),
From ddf78a83ff40a5f33697042fec17850ee62a08cb Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 23 Apr 2013 22:08:28 +0100
Subject: [PATCH 17/61] Add javadoc for the final unit test for filtering.
---
.../org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index e2b854d..8a12668 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -133,7 +133,7 @@ public void getAllPersonsEligibleToVote() {
}
/**
- * Uses Stream.filter() to find all the voters residing in a given district.
+ * Use Stream.filter() to find all the voters residing in a given district.
*
* The resulting collection is to be used for quick lookups to find if a given
* voter resides in a district. Performance measurements indicate we should
@@ -166,6 +166,12 @@ public void getAllPersonsEligibleToVote() {
assertThat(votersInHackney, containsInAnyOrder(aVoterWithId("HA7654"), aVoterWithId("HA2213")));
}
+ /**
+ * Use Stream.filter() to remove all the ballots that have been spoiled.
+ *
+ * @see ElectoralDistrict#unspoiledBallots(Set)
+ * @see Ballot#isSpoiled
+ */
@Test public void removeAllSpoiledBallots() {
Set votes = new HashSet<>(asList(
Ballot.voteFor(Party.LABOUR),
From c46911871b44c61bc6c39674188ed406399c450d Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 7 May 2013 19:20:33 +0100
Subject: [PATCH 18/61] Replace all anonymous implementations of FeatureMatcher
with call to factory method given an extractor function.
---
.../lambda/tutorial/Exercise_1_Test.java | 8 ++-----
.../lambda/tutorial/Exercise_2_Test.java | 24 ++++---------------
.../lambda/tutorial/util/FeatureMatchers.java | 23 ++++++++++++++++++
3 files changed, 30 insertions(+), 25 deletions(-)
create mode 100644 src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java
index a556cad..8330b2b 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java
@@ -4,7 +4,7 @@
import org.adoptopenjdk.lambda.tutorial.exercise1.Color;
import org.adoptopenjdk.lambda.tutorial.exercise1.Shape;
import org.adoptopenjdk.lambda.tutorial.exercise1.Shapes;
-import org.hamcrest.FeatureMatcher;
+import org.adoptopenjdk.lambda.tutorial.util.FeatureMatchers;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.junit.Test;
@@ -139,11 +139,7 @@ public void changeColorOfAllShapes_AND_buildStringShowingAllTheOldColors() {
// Test helpers
private static Matcher hasColor(Color color) {
- return new FeatureMatcher(Matchers.is(color), "has color", "color") {
- @Override protected Color featureValueOf(Shape shape) {
- return shape.getColor();
- }
- };
+ return FeatureMatchers.from(Matchers.is(color), "has color", "color", Shape::getColor);
}
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index 8a12668..19e9594 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -6,9 +6,8 @@
import org.adoptopenjdk.lambda.tutorial.exercise2.Person;
import org.adoptopenjdk.lambda.tutorial.exercise2.RegisteredVoter;
import org.adoptopenjdk.lambda.tutorial.exercise2.VotingRules;
-import org.hamcrest.FeatureMatcher;
+import org.adoptopenjdk.lambda.tutorial.util.FeatureMatchers;
import org.hamcrest.Matcher;
-import org.hamcrest.Matchers;
import org.junit.Test;
import java.util.ArrayList;
@@ -17,7 +16,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
@@ -26,7 +24,6 @@
import static java.util.Arrays.asList;
import static java.util.Arrays.binarySearch;
import static org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict.HACKNEY;
-import static org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict.votersIn;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
@@ -221,26 +218,15 @@ public void getAllPersonsEligibleToVote() {
// Test helpers
private static Matcher aPersonNamed(String name) {
- return featureMatcher(is(name), "a person", "name", p -> p.name);
+ return FeatureMatchers.from(is(name), "a person", "name", p -> p.name);
}
private static Matcher aVoterWithId(String name) {
- return featureMatcher(is(name), "a voter", "electorId", v -> v.electorId);
+ return FeatureMatchers.from(is(name), "a voter", "electorId", v -> v.electorId);
}
private static Matcher spoiled() {
- return featureMatcher(equalTo(true), "a spoiled ballot", "isSpoiled", b -> b.isSpoiled);
+ return FeatureMatchers.from(equalTo(true), "a spoiled ballot", "isSpoiled", b -> b.isSpoiled);
}
- private static Matcher featureMatcher(Matcher featureMatcher,
- String description,
- String name,
- Function extractor) {
- return new FeatureMatcher(featureMatcher, description, name) {
- @Override protected FEATURE featureValueOf(FROM t) {
- return extractor.apply(t);
- }
- };
- }
-
-}
+}
\ No newline at end of file
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java
new file mode 100644
index 0000000..9148d90
--- /dev/null
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java
@@ -0,0 +1,23 @@
+package org.adoptopenjdk.lambda.tutorial.util;
+
+import org.hamcrest.FeatureMatcher;
+import org.hamcrest.Matcher;
+
+import java.util.function.Function;
+
+public final class FeatureMatchers {
+
+ private FeatureMatchers() {}
+
+ public static Matcher from(Matcher featureMatcher,
+ String description,
+ String name,
+ Function extractor) {
+ return new FeatureMatcher(featureMatcher, description, name) {
+ @Override protected FEATURE featureValueOf(FROM t) {
+ return extractor.apply(t);
+ }
+ };
+ }
+
+}
From 2a0d0e863abad103caeb048c77e1f48bb60d83f2 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 7 May 2013 19:20:33 +0100
Subject: [PATCH 19/61] Replace all anonymous implementations of FeatureMatcher
with call to factory method given an extractor function.
---
.../lambda/tutorial/Exercise_1_Test.java | 8 ++-----
.../lambda/tutorial/Exercise_2_Test.java | 24 ++++---------------
.../lambda/tutorial/util/FeatureMatchers.java | 23 ++++++++++++++++++
3 files changed, 30 insertions(+), 25 deletions(-)
create mode 100644 src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java
index a556cad..8330b2b 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java
@@ -4,7 +4,7 @@
import org.adoptopenjdk.lambda.tutorial.exercise1.Color;
import org.adoptopenjdk.lambda.tutorial.exercise1.Shape;
import org.adoptopenjdk.lambda.tutorial.exercise1.Shapes;
-import org.hamcrest.FeatureMatcher;
+import org.adoptopenjdk.lambda.tutorial.util.FeatureMatchers;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.junit.Test;
@@ -139,11 +139,7 @@ public void changeColorOfAllShapes_AND_buildStringShowingAllTheOldColors() {
// Test helpers
private static Matcher hasColor(Color color) {
- return new FeatureMatcher(Matchers.is(color), "has color", "color") {
- @Override protected Color featureValueOf(Shape shape) {
- return shape.getColor();
- }
- };
+ return FeatureMatchers.from(Matchers.is(color), "has color", "color", Shape::getColor);
}
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index 8a12668..19e9594 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -6,9 +6,8 @@
import org.adoptopenjdk.lambda.tutorial.exercise2.Person;
import org.adoptopenjdk.lambda.tutorial.exercise2.RegisteredVoter;
import org.adoptopenjdk.lambda.tutorial.exercise2.VotingRules;
-import org.hamcrest.FeatureMatcher;
+import org.adoptopenjdk.lambda.tutorial.util.FeatureMatchers;
import org.hamcrest.Matcher;
-import org.hamcrest.Matchers;
import org.junit.Test;
import java.util.ArrayList;
@@ -17,7 +16,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
@@ -26,7 +24,6 @@
import static java.util.Arrays.asList;
import static java.util.Arrays.binarySearch;
import static org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict.HACKNEY;
-import static org.adoptopenjdk.lambda.tutorial.exercise2.ElectoralDistrict.votersIn;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
@@ -221,26 +218,15 @@ public void getAllPersonsEligibleToVote() {
// Test helpers
private static Matcher aPersonNamed(String name) {
- return featureMatcher(is(name), "a person", "name", p -> p.name);
+ return FeatureMatchers.from(is(name), "a person", "name", p -> p.name);
}
private static Matcher aVoterWithId(String name) {
- return featureMatcher(is(name), "a voter", "electorId", v -> v.electorId);
+ return FeatureMatchers.from(is(name), "a voter", "electorId", v -> v.electorId);
}
private static Matcher spoiled() {
- return featureMatcher(equalTo(true), "a spoiled ballot", "isSpoiled", b -> b.isSpoiled);
+ return FeatureMatchers.from(equalTo(true), "a spoiled ballot", "isSpoiled", b -> b.isSpoiled);
}
- private static Matcher featureMatcher(Matcher featureMatcher,
- String description,
- String name,
- Function extractor) {
- return new FeatureMatcher(featureMatcher, description, name) {
- @Override protected FEATURE featureValueOf(FROM t) {
- return extractor.apply(t);
- }
- };
- }
-
-}
+}
\ No newline at end of file
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java
new file mode 100644
index 0000000..9148d90
--- /dev/null
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java
@@ -0,0 +1,23 @@
+package org.adoptopenjdk.lambda.tutorial.util;
+
+import org.hamcrest.FeatureMatcher;
+import org.hamcrest.Matcher;
+
+import java.util.function.Function;
+
+public final class FeatureMatchers {
+
+ private FeatureMatchers() {}
+
+ public static Matcher from(Matcher featureMatcher,
+ String description,
+ String name,
+ Function extractor) {
+ return new FeatureMatcher(featureMatcher, description, name) {
+ @Override protected FEATURE featureValueOf(FROM t) {
+ return extractor.apply(t);
+ }
+ };
+ }
+
+}
From 6e138441cd0a52d5f3f48ee09f691909076a33ec Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 7 May 2013 21:45:58 +0100
Subject: [PATCH 20/61] Add a third exercise: mapping.
---
.../lambda/tutorial/Exercise_3_Test.java | 81 +++++++++++++++++++
1 file changed, 81 insertions(+)
create mode 100644 src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
new file mode 100644
index 0000000..2f5ea96
--- /dev/null
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
@@ -0,0 +1,81 @@
+package org.adoptopenjdk.lambda.tutorial;
+
+import org.junit.Test;
+
+import java.util.Collection;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Exercise 3 - Mapping
+ *
+ * Along with filter, map is one of the most common operations to perform.
+ *
+ * Consider this idiomatic Java code used to convert a list of strings to uppercase:
+ *
+ * List upperCaseStrings = new ArrayList<>();
+ * for (String s: mixedCaseStrings) {
+ * upperCaseStrings.add(s.toUpperCase());
+ * }
+ * return upperCaseStrings;
+ *
+ * As with the filter operation, map removes a lot of the boilerplate of this example by pushing the common code into
+ * the library, rather than having it repeated in your code. In this case, we are again:
+ * - constructing a new, empty, destination collection
+ * - iterating over the source collection
+ * - "doing something" to each element
+ * - adding the result to a new collection
+ *
+ * However, unlike filter, we are not just adding elements that pass a test, we are taking the element and creating some
+ * new value before adding it. The new value in the example above is taking the result of toUpperCase(). The result is a
+ * new list, of the same length, where each element is the result of mapping the source element. In post-JDK 8, you
+ * can express this operation like so:
+ *
+ * return mixedCaseStrings.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());
+ *
+ * As with the last exercise, we "open up" the Streams API by calling .stream() on a collection. This allows us to
+ * access the map() method.
+ *
+ * The map() method takes a function (java.util.function.Function) as an argument, and applies it to each element in a
+ * list. Since Function is an interface with a single abstract method, JDK 8 allows us to express it as a lambda. The
+ * single method on the Function type takes a single parameter of a certain type and returns a single result
+ * of another type. In this example, the input type is String, and the return type also happens to be String. It
+ * could be return any type, the resultant collection will be a collection of that type.
+ *
+ * Again we're transferring into a destination list by means of the Collectors.toList() method.
+ *
+ * So what is the result of both of implementations:
+ *
+ * If the variable 'mixedCaseStrings' in the example above looked like this:
+ *
+ * ["I", "am", "really", "enjoying", "lambda-tutorial"]
+ *
+ * The resultant variable 'upperCaseStrings', would like like this:
+ *
+ * ["I", "AM", "REALLY", "ENJOYING", "LAMBDA-TUTORIAL"]
+ *
+ * An important point to consider is that the map() method does not modify the original list, mixedCaseStrings still
+ * exists, as it was before. This makes it much easier to prevent bugs where mixedCaseStrings could be used elsewhere,
+ * perhaps later in the execution, or even concurrently in a different thread.
+ *
+ *
+ * The map operation is also known in other languages/libraries as: transform; collect.
+ *
+ * @see Collection#stream()
+ * @see Stream#map(Function)
+ * @see Function
+ * @see Collector
+ * @see Collectors
+ * @see Collectors#toList()
+ *
+ */
+public class Exercise_3_Test {
+
+ @Test public void tbd() {
+
+
+ }
+
+}
From 76936fa3f418d5013f8dbbd9d871c70737eb6a7a Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 7 May 2013 21:45:58 +0100
Subject: [PATCH 21/61] Add a third exercise: mapping.
---
.../lambda/tutorial/Exercise_3_Test.java | 81 +++++++++++++++++++
1 file changed, 81 insertions(+)
create mode 100644 src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
new file mode 100644
index 0000000..2f5ea96
--- /dev/null
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
@@ -0,0 +1,81 @@
+package org.adoptopenjdk.lambda.tutorial;
+
+import org.junit.Test;
+
+import java.util.Collection;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Exercise 3 - Mapping
+ *
+ * Along with filter, map is one of the most common operations to perform.
+ *
+ * Consider this idiomatic Java code used to convert a list of strings to uppercase:
+ *
+ * List upperCaseStrings = new ArrayList<>();
+ * for (String s: mixedCaseStrings) {
+ * upperCaseStrings.add(s.toUpperCase());
+ * }
+ * return upperCaseStrings;
+ *
+ * As with the filter operation, map removes a lot of the boilerplate of this example by pushing the common code into
+ * the library, rather than having it repeated in your code. In this case, we are again:
+ * - constructing a new, empty, destination collection
+ * - iterating over the source collection
+ * - "doing something" to each element
+ * - adding the result to a new collection
+ *
+ * However, unlike filter, we are not just adding elements that pass a test, we are taking the element and creating some
+ * new value before adding it. The new value in the example above is taking the result of toUpperCase(). The result is a
+ * new list, of the same length, where each element is the result of mapping the source element. In post-JDK 8, you
+ * can express this operation like so:
+ *
+ * return mixedCaseStrings.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());
+ *
+ * As with the last exercise, we "open up" the Streams API by calling .stream() on a collection. This allows us to
+ * access the map() method.
+ *
+ * The map() method takes a function (java.util.function.Function) as an argument, and applies it to each element in a
+ * list. Since Function is an interface with a single abstract method, JDK 8 allows us to express it as a lambda. The
+ * single method on the Function type takes a single parameter of a certain type and returns a single result
+ * of another type. In this example, the input type is String, and the return type also happens to be String. It
+ * could be return any type, the resultant collection will be a collection of that type.
+ *
+ * Again we're transferring into a destination list by means of the Collectors.toList() method.
+ *
+ * So what is the result of both of implementations:
+ *
+ * If the variable 'mixedCaseStrings' in the example above looked like this:
+ *
+ * ["I", "am", "really", "enjoying", "lambda-tutorial"]
+ *
+ * The resultant variable 'upperCaseStrings', would like like this:
+ *
+ * ["I", "AM", "REALLY", "ENJOYING", "LAMBDA-TUTORIAL"]
+ *
+ * An important point to consider is that the map() method does not modify the original list, mixedCaseStrings still
+ * exists, as it was before. This makes it much easier to prevent bugs where mixedCaseStrings could be used elsewhere,
+ * perhaps later in the execution, or even concurrently in a different thread.
+ *
+ *
+ * The map operation is also known in other languages/libraries as: transform; collect.
+ *
+ * @see Collection#stream()
+ * @see Stream#map(Function)
+ * @see Function
+ * @see Collector
+ * @see Collectors
+ * @see Collectors#toList()
+ *
+ */
+public class Exercise_3_Test {
+
+ @Test public void tbd() {
+
+
+ }
+
+}
From 373bb80aa1412dc2b6db6370395ff717d2fa44ee Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 7 May 2013 22:31:09 +0100
Subject: [PATCH 22/61] Add three tests for mapping behaviour.
---
.../lambda/tutorial/exercise3/Author.java | 35 +++++++++++
.../lambda/tutorial/exercise3/Book.java | 35 +++++++++++
.../lambda/tutorial/exercise3/Books.java | 25 ++++++++
.../lambda/tutorial/exercise3/Publisher.java | 26 ++++++++
.../lambda/tutorial/Exercise_3_Test.java | 63 ++++++++++++++++++-
5 files changed, 183 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Author.java
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Book.java
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Publisher.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Author.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Author.java
new file mode 100644
index 0000000..7e5ba57
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Author.java
@@ -0,0 +1,35 @@
+package org.adoptopenjdk.lambda.tutorial.exercise3;
+
+public final class Author {
+ public final String firstName;
+ public final String lastName;
+
+ public Author(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public String fullName() {
+ return firstName + " " + lastName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Author author = (Author) o;
+
+ if (!firstName.equals(author.firstName)) return false;
+ if (!lastName.equals(author.lastName)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = firstName.hashCode();
+ result = 31 * result + lastName.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Book.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Book.java
new file mode 100644
index 0000000..280e4f3
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Book.java
@@ -0,0 +1,35 @@
+package org.adoptopenjdk.lambda.tutorial.exercise3;
+
+public final class Book {
+ public final String title;
+ public final Author author;
+ public final Publisher publisher;
+
+ public Book(String title, Author author, Publisher publisher) {
+ this.title = title;
+ this.author = author;
+ this.publisher = publisher;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Book book = (Book) o;
+
+ if (!author.equals(book.author)) return false;
+ if (!publisher.equals(book.publisher)) return false;
+ if (!title.equals(book.title)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = title.hashCode();
+ result = 31 * result + author.hashCode();
+ result = 31 * result + publisher.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
new file mode 100644
index 0000000..b30f6af
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
@@ -0,0 +1,25 @@
+package org.adoptopenjdk.lambda.tutorial.exercise3;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class Books {
+ public static List titlesOf(List books) {
+ // [your code here]
+
+ return Collections.emptyList();
+ }
+
+ public static List namesOfAuthorsOf(List books) {
+ // [your code here]
+
+ return Collections.emptyList();
+ }
+
+ public static Collection publishersRepresentedBy(List books) {
+ // [your code here]
+
+ return Collections.emptySet();
+ }
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Publisher.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Publisher.java
new file mode 100644
index 0000000..1fabbc2
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Publisher.java
@@ -0,0 +1,26 @@
+package org.adoptopenjdk.lambda.tutorial.exercise3;
+
+public final class Publisher {
+ public final String name;
+
+ public Publisher(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Publisher publisher = (Publisher) o;
+
+ if (!name.equals(publisher.name)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
index 2f5ea96..41986cb 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
@@ -1,13 +1,22 @@
package org.adoptopenjdk.lambda.tutorial;
+import org.adoptopenjdk.lambda.tutorial.exercise3.Author;
+import org.adoptopenjdk.lambda.tutorial.exercise3.Book;
+import org.adoptopenjdk.lambda.tutorial.exercise3.Books;
+import org.adoptopenjdk.lambda.tutorial.exercise3.Publisher;
import org.junit.Test;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+
/**
* Exercise 3 - Mapping
*
@@ -63,6 +72,9 @@
*
* The map operation is also known in other languages/libraries as: transform; collect.
*
+ * The below tests can be made to pass using Stream's map method. Try to make them pass without using a loop, or adding
+ * to a new collection manually.
+ *
* @see Collection#stream()
* @see Stream#map(Function)
* @see Function
@@ -73,9 +85,58 @@
*/
public class Exercise_3_Test {
- @Test public void tbd() {
+ private final Author joshuaBloch = new Author("Joshua", "Bloch");
+ private final Author brianGoetz = new Author("Brian", "Goetz");
+ private final Author barryBurd = new Author("Barry", "Burd");
+
+ private final Publisher addisonWesley = new Publisher("Addison-Wesley");
+ private final Publisher johnWileyAndSons = new Publisher("John Wiley & Sons");
+
+ private final Book effectiveJava = new Book("Effective Java", joshuaBloch, addisonWesley);
+ private final Book javaConcurrencyInPractice = new Book("Java Concurrency In Practice", brianGoetz, addisonWesley);
+ private final Book javaForDummies = new Book("Java For Dummies", barryBurd, johnWileyAndSons);
+ private final List books = Arrays.asList(effectiveJava, javaConcurrencyInPractice, javaForDummies);
+
+ /**
+ * Use Stream.map() to convert a collection of books into a collection of their titles.
+ */
+ @Test
+ public void getAllBookTitles() {
+ assertThat(Books.titlesOf(books),
+ contains("Effective Java", "Java Concurrency In Practice", "Java For Dummies"));
+ }
+
+ /**
+ * Use Stream.map() to convert a collection of books into a collection of the author's full names.
+ *
+ * Note that it is possible to chain calls to map(). E.g. myCollection.map(...).map(...).collect(). That may come
+ * in useful when generating an author's full name.
+ *
+ * @see Author#fullName()
+ */
+ @Test
+ public void getNamesOfAuthorsOfBooks() {
+ assertThat(Books.namesOfAuthorsOf(books),
+ contains("Joshua Bloch", "Brian Goetz", "Barry Burd"));
+ }
+ /**
+ * Use Stream.map() to convert a collection of books into a collection of the distinct publishers represented within
+ * the given list of books. For example, given books A published by X, B published by Y, and C published by Y,
+ * return a collection consisting of X and Y.
+ *
+ * This can be done with a single stream().map(...).collect(...). Remember you can collect into collections other
+ * than a List.
+ *
+ * @see Publisher#hashCode()
+ * @see Publisher#equals(Object)
+ * @see Collectors#toSet()
+ */
+ @Test
+ public void getPublishersRepresentedByBooks() {
+ assertThat(Books.publishersRepresentedBy(books),
+ contains(addisonWesley, johnWileyAndSons));
}
}
From 54e1ebd9b28dc539b62f144e4416d219f5a5f60c Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 7 May 2013 22:31:09 +0100
Subject: [PATCH 23/61] Add three tests for mapping behaviour.
---
.../lambda/tutorial/exercise3/Author.java | 35 +++++++++++
.../lambda/tutorial/exercise3/Book.java | 35 +++++++++++
.../lambda/tutorial/exercise3/Books.java | 25 ++++++++
.../lambda/tutorial/exercise3/Publisher.java | 26 ++++++++
.../lambda/tutorial/Exercise_3_Test.java | 63 ++++++++++++++++++-
5 files changed, 183 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Author.java
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Book.java
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Publisher.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Author.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Author.java
new file mode 100644
index 0000000..7e5ba57
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Author.java
@@ -0,0 +1,35 @@
+package org.adoptopenjdk.lambda.tutorial.exercise3;
+
+public final class Author {
+ public final String firstName;
+ public final String lastName;
+
+ public Author(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public String fullName() {
+ return firstName + " " + lastName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Author author = (Author) o;
+
+ if (!firstName.equals(author.firstName)) return false;
+ if (!lastName.equals(author.lastName)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = firstName.hashCode();
+ result = 31 * result + lastName.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Book.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Book.java
new file mode 100644
index 0000000..280e4f3
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Book.java
@@ -0,0 +1,35 @@
+package org.adoptopenjdk.lambda.tutorial.exercise3;
+
+public final class Book {
+ public final String title;
+ public final Author author;
+ public final Publisher publisher;
+
+ public Book(String title, Author author, Publisher publisher) {
+ this.title = title;
+ this.author = author;
+ this.publisher = publisher;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Book book = (Book) o;
+
+ if (!author.equals(book.author)) return false;
+ if (!publisher.equals(book.publisher)) return false;
+ if (!title.equals(book.title)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = title.hashCode();
+ result = 31 * result + author.hashCode();
+ result = 31 * result + publisher.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
new file mode 100644
index 0000000..b30f6af
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
@@ -0,0 +1,25 @@
+package org.adoptopenjdk.lambda.tutorial.exercise3;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class Books {
+ public static List titlesOf(List books) {
+ // [your code here]
+
+ return Collections.emptyList();
+ }
+
+ public static List namesOfAuthorsOf(List books) {
+ // [your code here]
+
+ return Collections.emptyList();
+ }
+
+ public static Collection publishersRepresentedBy(List books) {
+ // [your code here]
+
+ return Collections.emptySet();
+ }
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Publisher.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Publisher.java
new file mode 100644
index 0000000..1fabbc2
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Publisher.java
@@ -0,0 +1,26 @@
+package org.adoptopenjdk.lambda.tutorial.exercise3;
+
+public final class Publisher {
+ public final String name;
+
+ public Publisher(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Publisher publisher = (Publisher) o;
+
+ if (!name.equals(publisher.name)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
index 2f5ea96..41986cb 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
@@ -1,13 +1,22 @@
package org.adoptopenjdk.lambda.tutorial;
+import org.adoptopenjdk.lambda.tutorial.exercise3.Author;
+import org.adoptopenjdk.lambda.tutorial.exercise3.Book;
+import org.adoptopenjdk.lambda.tutorial.exercise3.Books;
+import org.adoptopenjdk.lambda.tutorial.exercise3.Publisher;
import org.junit.Test;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+
/**
* Exercise 3 - Mapping
*
@@ -63,6 +72,9 @@
*
* The map operation is also known in other languages/libraries as: transform; collect.
*
+ * The below tests can be made to pass using Stream's map method. Try to make them pass without using a loop, or adding
+ * to a new collection manually.
+ *
* @see Collection#stream()
* @see Stream#map(Function)
* @see Function
@@ -73,9 +85,58 @@
*/
public class Exercise_3_Test {
- @Test public void tbd() {
+ private final Author joshuaBloch = new Author("Joshua", "Bloch");
+ private final Author brianGoetz = new Author("Brian", "Goetz");
+ private final Author barryBurd = new Author("Barry", "Burd");
+
+ private final Publisher addisonWesley = new Publisher("Addison-Wesley");
+ private final Publisher johnWileyAndSons = new Publisher("John Wiley & Sons");
+
+ private final Book effectiveJava = new Book("Effective Java", joshuaBloch, addisonWesley);
+ private final Book javaConcurrencyInPractice = new Book("Java Concurrency In Practice", brianGoetz, addisonWesley);
+ private final Book javaForDummies = new Book("Java For Dummies", barryBurd, johnWileyAndSons);
+ private final List books = Arrays.asList(effectiveJava, javaConcurrencyInPractice, javaForDummies);
+
+ /**
+ * Use Stream.map() to convert a collection of books into a collection of their titles.
+ */
+ @Test
+ public void getAllBookTitles() {
+ assertThat(Books.titlesOf(books),
+ contains("Effective Java", "Java Concurrency In Practice", "Java For Dummies"));
+ }
+
+ /**
+ * Use Stream.map() to convert a collection of books into a collection of the author's full names.
+ *
+ * Note that it is possible to chain calls to map(). E.g. myCollection.map(...).map(...).collect(). That may come
+ * in useful when generating an author's full name.
+ *
+ * @see Author#fullName()
+ */
+ @Test
+ public void getNamesOfAuthorsOfBooks() {
+ assertThat(Books.namesOfAuthorsOf(books),
+ contains("Joshua Bloch", "Brian Goetz", "Barry Burd"));
+ }
+ /**
+ * Use Stream.map() to convert a collection of books into a collection of the distinct publishers represented within
+ * the given list of books. For example, given books A published by X, B published by Y, and C published by Y,
+ * return a collection consisting of X and Y.
+ *
+ * This can be done with a single stream().map(...).collect(...). Remember you can collect into collections other
+ * than a List.
+ *
+ * @see Publisher#hashCode()
+ * @see Publisher#equals(Object)
+ * @see Collectors#toSet()
+ */
+ @Test
+ public void getPublishersRepresentedByBooks() {
+ assertThat(Books.publishersRepresentedBy(books),
+ contains(addisonWesley, johnWileyAndSons));
}
}
From 6d5644b0b1bd9d98da4657ce0085f446e2e707e8 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 7 May 2013 22:39:20 +0100
Subject: [PATCH 24/61] Make it clear method returns a set. Also use a feature
for a better test failure message.
---
.../lambda/tutorial/exercise3/Books.java | 4 ++--
.../lambda/tutorial/Exercise_3_Test.java | 19 +++++++++++++++----
2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
index b30f6af..0f428df 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
@@ -1,8 +1,8 @@
package org.adoptopenjdk.lambda.tutorial.exercise3;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
public class Books {
public static List titlesOf(List books) {
@@ -17,7 +17,7 @@ public static List namesOfAuthorsOf(List books) {
return Collections.emptyList();
}
- public static Collection publishersRepresentedBy(List books) {
+ public static Set publishersRepresentedBy(List books) {
// [your code here]
return Collections.emptySet();
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
index 41986cb..2447243 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
@@ -4,6 +4,8 @@
import org.adoptopenjdk.lambda.tutorial.exercise3.Book;
import org.adoptopenjdk.lambda.tutorial.exercise3.Books;
import org.adoptopenjdk.lambda.tutorial.exercise3.Publisher;
+import org.adoptopenjdk.lambda.tutorial.util.FeatureMatchers;
+import org.hamcrest.Matcher;
import org.junit.Test;
import java.util.Arrays;
@@ -15,7 +17,8 @@
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.equalTo;
/**
* Exercise 3 - Mapping
@@ -83,6 +86,7 @@
* @see Collectors#toList()
*
*/
+@SuppressWarnings("unchecked")
public class Exercise_3_Test {
private final Author joshuaBloch = new Author("Joshua", "Bloch");
@@ -104,7 +108,7 @@ public class Exercise_3_Test {
@Test
public void getAllBookTitles() {
assertThat(Books.titlesOf(books),
- contains("Effective Java", "Java Concurrency In Practice", "Java For Dummies"));
+ containsInAnyOrder("Effective Java", "Java Concurrency In Practice", "Java For Dummies"));
}
/**
@@ -118,7 +122,7 @@ public void getAllBookTitles() {
@Test
public void getNamesOfAuthorsOfBooks() {
assertThat(Books.namesOfAuthorsOf(books),
- contains("Joshua Bloch", "Brian Goetz", "Barry Burd"));
+ containsInAnyOrder("Joshua Bloch", "Brian Goetz", "Barry Burd"));
}
/**
@@ -136,7 +140,14 @@ public void getNamesOfAuthorsOfBooks() {
@Test
public void getPublishersRepresentedByBooks() {
assertThat(Books.publishersRepresentedBy(books),
- contains(addisonWesley, johnWileyAndSons));
+ containsInAnyOrder(publisherNamed("Addison-Wesley"), publisherNamed("John Wiley & Sons")));
+ }
+
+
+ // Test helpers
+
+ private static Matcher publisherNamed(String name) {
+ return FeatureMatchers.from(equalTo(name), "is named", "name", p -> p.name);
}
}
From 22f7675b4687c79a178d845dc15582f53c13a2fd Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 7 May 2013 22:39:20 +0100
Subject: [PATCH 25/61] Make it clear method returns a set. Also use a feature
for a better test failure message.
---
.../lambda/tutorial/exercise3/Books.java | 4 ++--
.../lambda/tutorial/Exercise_3_Test.java | 19 +++++++++++++++----
2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
index b30f6af..0f428df 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
@@ -1,8 +1,8 @@
package org.adoptopenjdk.lambda.tutorial.exercise3;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
public class Books {
public static List titlesOf(List books) {
@@ -17,7 +17,7 @@ public static List namesOfAuthorsOf(List books) {
return Collections.emptyList();
}
- public static Collection publishersRepresentedBy(List books) {
+ public static Set publishersRepresentedBy(List books) {
// [your code here]
return Collections.emptySet();
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
index 41986cb..2447243 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
@@ -4,6 +4,8 @@
import org.adoptopenjdk.lambda.tutorial.exercise3.Book;
import org.adoptopenjdk.lambda.tutorial.exercise3.Books;
import org.adoptopenjdk.lambda.tutorial.exercise3.Publisher;
+import org.adoptopenjdk.lambda.tutorial.util.FeatureMatchers;
+import org.hamcrest.Matcher;
import org.junit.Test;
import java.util.Arrays;
@@ -15,7 +17,8 @@
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.equalTo;
/**
* Exercise 3 - Mapping
@@ -83,6 +86,7 @@
* @see Collectors#toList()
*
*/
+@SuppressWarnings("unchecked")
public class Exercise_3_Test {
private final Author joshuaBloch = new Author("Joshua", "Bloch");
@@ -104,7 +108,7 @@ public class Exercise_3_Test {
@Test
public void getAllBookTitles() {
assertThat(Books.titlesOf(books),
- contains("Effective Java", "Java Concurrency In Practice", "Java For Dummies"));
+ containsInAnyOrder("Effective Java", "Java Concurrency In Practice", "Java For Dummies"));
}
/**
@@ -118,7 +122,7 @@ public void getAllBookTitles() {
@Test
public void getNamesOfAuthorsOfBooks() {
assertThat(Books.namesOfAuthorsOf(books),
- contains("Joshua Bloch", "Brian Goetz", "Barry Burd"));
+ containsInAnyOrder("Joshua Bloch", "Brian Goetz", "Barry Burd"));
}
/**
@@ -136,7 +140,14 @@ public void getNamesOfAuthorsOfBooks() {
@Test
public void getPublishersRepresentedByBooks() {
assertThat(Books.publishersRepresentedBy(books),
- contains(addisonWesley, johnWileyAndSons));
+ containsInAnyOrder(publisherNamed("Addison-Wesley"), publisherNamed("John Wiley & Sons")));
+ }
+
+
+ // Test helpers
+
+ private static Matcher publisherNamed(String name) {
+ return FeatureMatchers.from(equalTo(name), "is named", "name", p -> p.name);
}
}
From 46b3eee2f99b6923f6a865e23701c90899a33662 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 7 May 2013 22:40:57 +0100
Subject: [PATCH 26/61] Make tests pass for JDK 8.
---
.../lambda/tutorial/exercise3/Books.java | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
index 0f428df..5a20349 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
@@ -3,23 +3,19 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
public class Books {
- public static List titlesOf(List books) {
- // [your code here]
- return Collections.emptyList();
+ public static List titlesOf(List books) {
+ return books.stream().map(b -> b.title).collect(Collectors.toList());
}
public static List namesOfAuthorsOf(List books) {
- // [your code here]
-
- return Collections.emptyList();
+ return books.stream().map(b -> b.author).map(Author::fullName).collect(Collectors.toList());
}
public static Set publishersRepresentedBy(List books) {
- // [your code here]
-
- return Collections.emptySet();
+ return books.stream().map(b -> b.publisher).collect(Collectors.toSet());
}
}
From 3fe33e1dba35b84e84722d6f728c01891501b2eb Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Tue, 7 May 2013 22:46:39 +0100
Subject: [PATCH 27/61] Make tests pass for pre-JDK 8.
---
.../lambda/tutorial/exercise3/Books.java | 24 +++++++++++++------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
index 0f428df..04170dd 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
@@ -1,25 +1,35 @@
package org.adoptopenjdk.lambda.tutorial.exercise3;
-import java.util.Collections;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Books {
public static List titlesOf(List books) {
- // [your code here]
+ List titles = new ArrayList<>();
+ for (Book b: books) {
+ titles.add(b.title);
+ }
- return Collections.emptyList();
+ return titles;
}
public static List namesOfAuthorsOf(List books) {
- // [your code here]
+ List fullNames = new ArrayList<>();
+ for (Book b: books) {
+ fullNames.add(b.author.fullName());
+ }
- return Collections.emptyList();
+ return fullNames;
}
public static Set publishersRepresentedBy(List books) {
- // [your code here]
+ Set publishers = new HashSet<>();
+ for (Book b: books) {
+ publishers.add(b.publisher);
+ }
- return Collections.emptySet();
+ return publishers;
}
}
From 2318613ba31115f692d59a8ecc2457edaa1acd98 Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sat, 11 May 2013 11:42:37 +0100
Subject: [PATCH 28/61] Add javadoc to each of the methods under test to repeat
what is expected of them.
---
.../lambda/tutorial/exercise1/Shapes.java | 40 +++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
index 1839713..24732e5 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
@@ -12,14 +12,54 @@
*/
public class Shapes {
+ /**
+ * Changes the color of all the given shapes, setting to newColor.
+ *
+ * Example:
+ * given a list containing [BLUE shape, GREEN shape, BLACK shape]
+ * when this method is called with that list and the color RED
+ * then the list will contain [RED shape, RED shape, RED shape]
+ *
+ * @see Shape#setColor(Color)
+ */
public static void colorAll(List shapes, Color newColor) {
shapes.forEach(s -> s.setColor(newColor));
}
+ /**
+ * Creates a String representation of all the given shapes, appending to the given
+ * stringBuilder.
+ *
+ * Uses Shape#toString to create the String representation of each shape.
+ *
+ * Example:
+ * given a list containing [BLUE shape, GREEN shape, BLACK shape]
+ * when this method is called with that list and an empty StringBuilder
+ * then the StringBuilder's toString method will return "[BLUE][GREEN][BLACK]"
+ *
+ * @see Shape#toString()
+ */
public static void makeStringOfAllColors(List shapes, StringBuilder stringBuilder) {
shapes.forEach(s -> stringBuilder.append(s));
}
+
+ /**
+ * Changes the color of each given shape to newColor, appending a String representation of the color of all the
+ * shapes, as they were before they were changed.
+ *
+ *
+ * Example:
+ * given a list containing [BLUE shape, GREEN shape, BLACK shape]
+ * when this method is called with that list, the color RED, and an empty StringBuilder
+ * then the list will contain [RED shape, RED shape, RED shape]
+ * and the StringBuilder's toString method will return "[BLUE][GREEN][BLACK]"
+ *
+ * This operation is performed in one pass over the shapes List.
+ *
+ * @see Shape#setColor(Color)
+ * @see Shape#toString()
+ */
public static void changeColorAndMakeStringOfOldColors(List shapes, Color newColor, StringBuilder stringBuilder) {
shapes.forEach(s -> { stringBuilder.append("[" + s.getColor() + "]"); s.setColor(newColor); });
From 7f0affcab27afbd44ded4f572f603bd6dcbaeafe Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sat, 11 May 2013 11:42:37 +0100
Subject: [PATCH 29/61] Add javadoc to each of the methods under test to repeat
what is expected of them.
---
.../lambda/tutorial/exercise1/Shapes.java | 40 +++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
index f49873b..f8f0cf0 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
@@ -12,18 +12,58 @@
*/
public class Shapes {
+ /**
+ * Changes the color of all the given shapes, setting to newColor.
+ *
+ * Example:
+ * given a list containing [BLUE shape, GREEN shape, BLACK shape]
+ * when this method is called with that list and the color RED
+ * then the list will contain [RED shape, RED shape, RED shape]
+ *
+ * @see Shape#setColor(Color)
+ */
public static void colorAll(List shapes, Color newColor) {
for (Shape s: shapes) {
s.setColor(newColor);
}
}
+ /**
+ * Creates a String representation of all the given shapes, appending to the given
+ * stringBuilder.
+ *
+ * Uses Shape#toString to create the String representation of each shape.
+ *
+ * Example:
+ * given a list containing [BLUE shape, GREEN shape, BLACK shape]
+ * when this method is called with that list and an empty StringBuilder
+ * then the StringBuilder's toString method will return "[BLUE][GREEN][BLACK]"
+ *
+ * @see Shape#toString()
+ */
public static void makeStringOfAllColors(List shapes, StringBuilder stringBuilder) {
for (Shape s: shapes) {
stringBuilder.append(s);
}
}
+
+ /**
+ * Changes the color of each given shape to newColor, appending a String representation of the color of all the
+ * shapes, as they were before they were changed.
+ *
+ *
+ * Example:
+ * given a list containing [BLUE shape, GREEN shape, BLACK shape]
+ * when this method is called with that list, the color RED, and an empty StringBuilder
+ * then the list will contain [RED shape, RED shape, RED shape]
+ * and the StringBuilder's toString method will return "[BLUE][GREEN][BLACK]"
+ *
+ * This operation is performed in one pass over the shapes List.
+ *
+ * @see Shape#setColor(Color)
+ * @see Shape#toString()
+ */
public static void changeColorAndMakeStringOfOldColors(List shapes, Color newColor, StringBuilder stringBuilder) {
for (Shape s: shapes) {
stringBuilder.append("[" + s.getColor() + "]");
From de271c2e313d778b682e48d26ae0a6087f7a1f1e Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sun, 12 May 2013 22:32:07 +0100
Subject: [PATCH 30/61] Merge in changes from karianna's fixes on master.
---
.../lambda/tutorial/exercise1/Shapes.java | 14 ++++++++---
.../tutorial/exercise2/ElectoralDistrict.java | 25 +++++++++++++------
.../tutorial/exercise2/VotingRules.java | 11 +++++---
.../lambda/tutorial/exercise3/Books.java | 23 ++++++++++++++---
4 files changed, 55 insertions(+), 18 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
index df4f1ed..4bc9bac 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise1/Shapes.java
@@ -23,7 +23,9 @@ public class Shapes {
* @see Shape#setColor(Color)
*/
public static void colorAll(List shapes, Color newColor) {
- shapes.forEach(s -> s.setColor(newColor));
+ for (Shape s: shapes) {
+ s.setColor(newColor);
+ }
}
/**
@@ -40,7 +42,9 @@ public static void colorAll(List shapes, Color newColor) {
* @see Shape#toString()
*/
public static void makeStringOfAllColors(List shapes, StringBuilder stringBuilder) {
- shapes.forEach(s -> stringBuilder.append(s));
+ for (Shape s: shapes) {
+ stringBuilder.append(s);
+ }
}
/**
@@ -61,7 +65,9 @@ public static void makeStringOfAllColors(List shapes, StringBuilder strin
* @see Shape#toString()
*/
public static void changeColorAndMakeStringOfOldColors(List shapes, Color newColor, StringBuilder stringBuilder) {
- shapes.forEach(s -> { stringBuilder.append(s.toString()); s.setColor(newColor); });
-
+ for (Shape s: shapes) {
+ stringBuilder.append(s.toString());
+ s.setColor(newColor);
+ }
}
}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
index cfbda10..eb407a7 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
@@ -2,6 +2,7 @@
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
@@ -27,17 +28,25 @@ public enum ElectoralDistrict {
}
public static Set votersIn(ElectoralDistrict district, Collection voters) {
- Set fromDistrict = voters.stream()
- .filter(v -> v.getElectorId().startsWith(district.prefix))
- .collect(Collectors.toSet());
-
- return Collections.unmodifiableSet(fromDistrict);
+ Set votersInDistrict = new HashSet<>();
+ for (RegisteredVoter v: voters) {
+ if (v.getElectorId().startsWith(district.prefix)) {
+ votersInDistrict.add(v);
+ }
+ }
+
+ return Collections.unmodifiableSet(votersInDistrict);
}
public static Set unspoiledBallots(Set votes) {
- return votes.stream()
- .filter(v -> !v.isSpoiled())
- .collect(Collectors.toSet());
+ Set unspoiledBallots = new HashSet<>();
+ for (Ballot v: votes) {
+ if (!v.isSpoiled()) {
+ unspoiledBallots.add(v);
+ }
+ }
+
+ return unspoiledBallots;
}
}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java
index 9c18ca1..a3a38e8 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/VotingRules.java
@@ -1,11 +1,16 @@
package org.adoptopenjdk.lambda.tutorial.exercise2;
+import java.util.ArrayList;
import java.util.List;
public class VotingRules {
public static List eligibleVoters(List potentialVoters, int legalAgeOfVoting) {
- return potentialVoters.stream()
- .filter(p -> p.getAge() >= legalAgeOfVoting)
- .collect(Collectors.toList());
+ List eligible = new ArrayList<>();
+ for (Person p: potentialVoters) {
+ if (p.getAge() >= legalAgeOfVoting) {
+ eligible.add(p);
+ }
+ }
+ return eligible;
}
}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
index f5a835a..812750c 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
@@ -1,5 +1,7 @@
package org.adoptopenjdk.lambda.tutorial.exercise3;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -7,14 +9,29 @@
public class Books {
public static List titlesOf(List books) {
- return books.stream().map(b -> b.title).collect(Collectors.toList());
+ List titles = new ArrayList<>();
+ for (Book b: books) {
+ titles.add(b.title);
+ }
+
+ return titles;
}
public static List namesOfAuthorsOf(List books) {
- return books.stream().map(b -> b.author).map(Author::fullName).collect(Collectors.toList());
+ List fullNames = new ArrayList<>();
+ for (Book b: books) {
+ fullNames.add(b.author.fullName());
+ }
+
+ return fullNames;
}
public static Set publishersRepresentedBy(List books) {
- return books.stream().map(b -> b.publisher).collect(Collectors.toSet());
+ Set publishers = new HashSet<>();
+ for (Book b: books) {
+ publishers.add(b.publisher);
+ }
+
+ return publishers;
}
}
From 522e1a6e6b73f8da4ba8e792f02c528f0b885f92 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Mon, 2 Sep 2013 23:06:32 +0100
Subject: [PATCH 31/61] Update build information used in readme.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index d59ff77..a70b596 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,6 @@ The current tutorial is known to work with the following JDK build:
|JDK Build Number|Released On |
|:---------------|:---------- |
-|b88 |May 09, 2013|
+|b105 |Aug 26, 2013|
lambda-tutorial will try to track against the newest version available. If you find that you are working with a newer version of the Lambda JDK and the tutorial does not compile or run, please file an issue.
From 35a0655e18c880808be0d668c9f5c2aa78a57836 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Mon, 2 Sep 2013 23:34:33 +0100
Subject: [PATCH 32/61] Now that there's a hook to wrap a computed stream,
remove the comment about there being no way to do that.
---
.../org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index 267cd7b..4803a9e 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -38,6 +38,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
@@ -242,13 +243,10 @@ public void getAllPersonsEligibleToVote() {
* Ensure that the Set returned cannot be modified by callers by wrapping the result
* in Collections.unmodifiableSet().
*
- *
- * The Streams API does not provide a way to wrap the final result, as one of its operations. So just wrap the
- * result in an unmodifiableSet yourself. Sometimes it's just as important to know what an API _doesn't_ do.
- *
* @throws ClassNotFoundException If the lambdas binary build no longer contains the class
* @see Stream#collect(java.util.stream.Collector)
- * @see java.util.stream.Collectors#toSet()
+ * @see Collectors#collectingAndThen(Collector, Function)
+ * @see Collectors#toSet()
* @see Collections#unmodifiableSet(java.util.Set)
*/
@Test public void setOfVotersInDistrictInUnmodifiableSet() throws ClassNotFoundException {
From de8ab52744d4f389f473c1b4290701e676b86b3f Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Thu, 3 Oct 2013 20:27:47 +0100
Subject: [PATCH 33/61] GrahamA: tested with most recent jdk ea build.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index a70b596..c8123ae 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,6 @@ The current tutorial is known to work with the following JDK build:
|JDK Build Number|Released On |
|:---------------|:---------- |
-|b105 |Aug 26, 2013|
+|ea b109 |Sep 26, 2013|
lambda-tutorial will try to track against the newest version available. If you find that you are working with a newer version of the Lambda JDK and the tutorial does not compile or run, please file an issue.
From 58f254b969dadd26177fd077a26e322bd101a7d4 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Thu, 3 Oct 2013 21:41:54 +0100
Subject: [PATCH 34/61] Describe each of the remaining kinds of method
references available.
Conflicts:
src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
---
.../lambda/tutorial/exercise4/Document.java | 29 ++++
.../lambda/tutorial/exercise4/Printers.java | 38 +++++
.../lambda/tutorial/Exercise_4_Test.java | 138 ++++++++++++++++++
3 files changed, 205 insertions(+)
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java
create mode 100644 src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
new file mode 100644
index 0000000..f63a873
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
@@ -0,0 +1,29 @@
+package org.adoptopenjdk.lambda.tutorial.exercise4;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public final class Document {
+ private final List pages;
+
+ public Document(List pages) {
+ this.pages = Collections.unmodifiableList(new ArrayList<>(pages));
+ }
+
+ public String getPageContent(Integer pageNumber) {
+ return this.pages.get(pageNumber).getContent();
+ }
+
+ public static final class Page {
+ private final String content;
+
+ public Page(String content) {
+ this.content = content;
+ }
+
+ public String getContent() {
+ return this.content;
+ }
+ }
+}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java
new file mode 100644
index 0000000..4bd6d76
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java
@@ -0,0 +1,38 @@
+package org.adoptopenjdk.lambda.tutorial.exercise4;
+
+import java.util.Arrays;
+import java.util.List;
+import static java.util.stream.Collectors.toList;
+import java.util.stream.Stream;
+
+import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
+
+public class Printers {
+ public static void print(String s) {
+ System.out.println(s);
+ }
+
+ public static void printPages(Document doc, Integer... pageNumbers) {
+ Arrays.stream(pageNumbers).map(doc::getPageContent).forEach(Printers::print);
+ }
+
+ public static Stream createPagesFrom(Stream contents) {
+ return contents.map(Page::new);
+ }
+
+
+ public static void main(String... args) {
+ Page p1 = new Page("this is the first page");
+ Page p2 = new Page("this is the second page");
+
+ Document myDocument = new Document(Arrays.asList(p1, p2));
+
+ Printers.printPages(myDocument, 0, 1);
+
+ List pages = Arrays.asList(p1, p2);
+ pages.stream().map(Page::getContent).forEach(Printers::print);
+
+ Stream pagesFromContent = createPagesFrom(Arrays.asList("a", "b").stream());
+ System.out.println(pagesFromContent.collect(toList()));
+ }
+}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
new file mode 100644
index 0000000..e25c601
--- /dev/null
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
@@ -0,0 +1,138 @@
+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
+ * .
+ * #L%
+ */
+
+import java.util.function.Consumer;
+
+/**
+ * Exercise 4 - Method References
+ *
+ * Method references are another syntactic addition to JDK 8. They are intended to be used in lambda expressions,
+ * preventing unnecessary boilerplate. Consider the following lambda from an earlier test:
+ * books.stream().map(b -> b.getTitle()). Here the lambda does nothing more than invoke a method on each
+ * element of the stream. This can be written alternatively as: books.stream().map(Book::getTitle). Both
+ * forms are equivalent.
+ *
+ *
+ * The parameter list and return type when using a method reference must match the signature of the method. A mismatch
+ * between the two will result in a compile error, just as invoking a method with parameters or return type with an
+ * incorrect type will cause a compile error.
+ * For example, consider the following code:
+ *
+ * public class Printers {
+ * public static void print(String s) {...}
+ * }
+ *
+ * Note that the printNumber method takes a single String parameter, and has a void return
+ * type. Although declared using different type names, this is the same signature as {@link Consumer}, the functional
+ * interface that's passed to {@link Iterable#forEach(Consumer)}. Since they match, we can use a reference to the
+ * print method when invoking forEach on a list of Strings, like so:
+ *
+ * Arrays.asList("a", "b", "c").forEach(Printers::print)
+ *
+ * If we changed the signature of print to public static void print(String s, String t)
+ * the use of the method reference would no longer compile, with an error message pointing to the mismatch in argument
+ * lists.
+ *
+ *
+ * There are four different kinds of methods that can be used with the method reference syntax:
+ *
+ * - Static method belonging to a particular class
+ * - Instance method bound to a particular object instance
+ * - Instance method bound to a particular class
+ * - Constructor belonging to a particular class
+ *
+ *
+ * We'll discuss each in turn:
+ *
+ * Static method belonging to a particular class
+ *
+ * In the previous example, the Printers::print method reference is to a static method ('print') belonging
+ * to the Printers class. Here the argument list of the lambda must match the argument list of the method, the first
+ * argument to the lambda is the first argument passed into the method.
+ *
+ *
+ *
+ * Instance method bound to a particular object instance
+ *
+ * It's possible to use a method invoked an a specific instance of a class. Consider the following code:
+ *
+ * public class Document {
+ * // field, constructor, etc
+ *
+ * public String getPageContent(int pageNumber) {
+ * return this.pages.get(pageNumber).getContent();
+ * }
+ * }
+ *
+ * public static void printPages(Document doc, int[] pageNumbers) {
+ * Arrays.stream(pageNumbers).map(doc::getPageContent).forEach(Printers::print);
+ * }
+ *
+ *
+ * In this case, when the map operation runs, the method getPageContent will
+ * be invoked on the doc instance. Regardless of the current page number at that point
+ * of the stream, it will always be transformed by calling the equivalent of doc.getPageContent(i).
+ *
+ *
+ * Instance method belonging to a particular class
+ *
+ * When iterating over a stream of objects, you can invoke a method on each object by using an instance method
+ * reference. As in this code:
+ *
+ * public static void printDocuments(List<Page> pages) {
+ * pages.stream().map(Page::getContent).forEach(Printers::print);
+ * }
+ *
+ * In this case the method getContent will still be invoked on an instance of Page, however,
+ * it will be invoked on each Page instance that is mapped over.
+ *
+ *
+ * Constructor belonging to a particular class
+ * By now, we know how to use method references for static methods and instance methods, that leaves an odd case:
+ * constructors.
+ *
+ * While we don't invoke a constructor like a static method, it is useful to think of it that way. Currently we write:
+ * Page p = new Page("content"); but imagine we changed the syntax of the Java language to allow this:
+ * Page p = Page.new("content");. We can consider the Page.new method to have the exact
+ * semantics of a constructor, that is, use a reference to the class object to invoke the constructor method and return
+ * the newly created instance as the result of new.
+ *
+ * With that in mind, consider the following code:
+ *
+ * public static Stream<Page> createPagesFrom(Stream<String> contents) {
+ * return contents.map(Page::new).
+ * }
+ *
+ * The method will return a Stream of newly constructed Page objects. new is
+ * still a special keyword in Java, but can now be used in the method reference construct. Note that just like other
+ * method references, the method signature of the constructor must match the types fed by the map
+ * operation.
+ *
+ *
+ */
+@SuppressWarnings("unchecked")
+public class Exercise_4_Test {
+
+
+}
From 71e69283f0b4403b94073a8c83f53335885c1efe Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Fri, 4 Oct 2013 00:51:02 +0100
Subject: [PATCH 35/61] Ignore more intellij files.
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index fc2a2f9..7fe7beb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
*iml
*ipr
*iws
+*eml
# Eclipse
.classpath
From 90e8717525946314f0e57c407fccef0f3704578a Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Fri, 4 Oct 2013 00:51:26 +0100
Subject: [PATCH 36/61] Introduce asm to help verify that method references
have been used.
---
pom.xml | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index e13fb1f..688d5cc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -87,6 +87,12 @@
1.3
test
+
+ org.ow2.asm
+ asm-debug-all
+ 5.0_ALPHA
+ test
+
@@ -103,8 +109,13 @@
org.hamcrest
hamcrest-library
+
+ org.ow2.asm
+ asm-debug-all
+
-
+
+
From 0697cd1c13f607f10d3397eb319b052f06e0ee94 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Fri, 4 Oct 2013 00:53:14 +0100
Subject: [PATCH 37/61] First example complete. A matcher that checks you're
using method references by discovering the java source file from the class,
scans the filesystem, and reads the source to check there's a :: before the
method name. Phew.
---
.../lambda/tutorial/exercise4/Document.java | 8 +-
.../lambda/tutorial/Documents.java | 42 +++++
.../lambda/tutorial/Exercise_4_Test.java | 35 ++++
.../util/CodeUsesMethodReferencesMatcher.java | 169 ++++++++++++++++++
4 files changed, 253 insertions(+), 1 deletion(-)
create mode 100644 src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
create mode 100644 src/test/java/org/adoptopenjdk/lambda/tutorial/util/CodeUsesMethodReferencesMatcher.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
index f63a873..fda540f 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
@@ -5,9 +5,11 @@
import java.util.List;
public final class Document {
+ private final String title;
private final List pages;
- public Document(List pages) {
+ public Document(String title, List pages) {
+ this.title = title;
this.pages = Collections.unmodifiableList(new ArrayList<>(pages));
}
@@ -15,6 +17,10 @@ public String getPageContent(Integer pageNumber) {
return this.pages.get(pageNumber).getContent();
}
+ public String getTitle() {
+ return this.title;
+ }
+
public static final class Page {
private final String content;
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
new file mode 100644
index 0000000..af52656
--- /dev/null
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
@@ -0,0 +1,42 @@
+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
+ * .
+ * #L%
+ */
+
+import org.adoptopenjdk.lambda.tutorial.exercise4.Document;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+public class Documents {
+
+ /**
+ * Return the titles from a list of documents.
+ */
+ public static List titlesOf(Document... documents) {
+ return Arrays.stream(documents)
+ .map(d -> d.getTitle())
+ .collect(toList());
+ }
+}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
index e25c601..931bf67 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
@@ -22,8 +22,17 @@
* #L%
*/
+import org.adoptopenjdk.lambda.tutorial.exercise4.Document;
+import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+import java.util.Arrays;
import java.util.function.Consumer;
+import static org.adoptopenjdk.lambda.tutorial.util.CodeUsesMethodReferencesMatcher.usesMethodReferences;
+import static org.hamcrest.MatcherAssert.assertThat;
+
/**
* Exercise 4 - Method References
*
@@ -109,6 +118,7 @@
*
*
* Constructor belonging to a particular class
+ *
* By now, we know how to use method references for static methods and instance methods, that leaves an odd case:
* constructors.
*
@@ -134,5 +144,30 @@
@SuppressWarnings("unchecked")
public class Exercise_4_Test {
+ /**
+ * The Documents class has a method which transforms a list of Document into a list of
+ * their titles. The implementation has already been filled out, but it uses a lambda, as in:
+ * .map(document -> document.getTitle())
+ *
+ * Instead of using a lambda, use a method reference instead.
+ *
+ * @see Documents#titlesOf(Document...)
+ * @see Document#getTitle()
+ *
+ */
+ @Test
+ public void getListOfDocumentTitlesUsingInstanceMethodReference() {
+ Document expenses = new Document("My Expenses",
+ Arrays.asList(new Page("LJC Open Conference ticket: £25"), new Page("Beer stipend: £100")));
+ Document toDoList = new Document("My ToDo List",
+ Arrays.asList(new Page("Build a todo app"), new Page("Pick up dry cleaning")));
+ Document certificates = new Document("My Certificates",
+ Arrays.asList(new Page("Oracle Certified Professional"), new Page("Swimming 10m")));
+
+ assertThat(Documents.titlesOf(expenses, toDoList, certificates),
+ Matchers.contains("My Expenses", "My ToDo List", "My Certificates"));
+ assertThat(Documents.class, usesMethodReferences("getTitle"));
+
+ }
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/util/CodeUsesMethodReferencesMatcher.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/util/CodeUsesMethodReferencesMatcher.java
new file mode 100644
index 0000000..70a9ba2
--- /dev/null
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/util/CodeUsesMethodReferencesMatcher.java
@@ -0,0 +1,169 @@
+package org.adoptopenjdk.lambda.tutorial.util;
+
+/*
+ * #%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
+ * .
+ * #L%
+ */
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Optional;
+
+import static java.util.stream.Collectors.toList;
+
+public final class CodeUsesMethodReferencesMatcher extends TypeSafeDiagnosingMatcher> {
+
+ private final String methodName;
+
+ private CodeUsesMethodReferencesMatcher(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public static CodeUsesMethodReferencesMatcher usesMethodReferences(String methodName) {
+ return new CodeUsesMethodReferencesMatcher(methodName);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("a source file using a method reference to invoke ").appendValue(methodName);
+ }
+
+
+ @Override
+ protected boolean matchesSafely(Class> clazz, Description mismatchDescription) {
+ try {
+ Optional sourceFileContent = getSourceContent(clazz);
+ return sourceFileContent.map(c -> usesMethodReference(c, mismatchDescription)).orElseGet(() -> {
+ mismatchDescription.appendText("could not read source file to discover if you used method references.");
+ return false;
+ });
+ } catch (IOException e) {
+ mismatchDescription.appendText("could not read source file to discover if you used method references.");
+ mismatchDescription.appendValue(e);
+ return false;
+ }
+ }
+
+ private boolean usesMethodReference(String sourceCode, Description mismatchDescription) {
+ if (sourceCode.contains("::"+methodName)) {
+ return true;
+ } else {
+ mismatchDescription.appendText("source code did not use a method reference to invoke " + methodName + ". ");
+ context(sourceCode, methodName, mismatchDescription);
+ return false;
+ }
+ }
+
+ private void context(String sourceCode, String methodName, Description mismatchDescription) {
+ if (!sourceCode.contains(methodName)) {
+ mismatchDescription.appendText("You did not appear to invoke the method at all.");
+ } else {
+ String[] lines = sourceCode.split("\\n");
+ mismatchDescription.appendText("Actual invocations: ");
+ mismatchDescription.appendValueList("[", ",", "]",
+ Arrays.stream(lines).filter(l -> l.contains(methodName)).map(String::trim).collect(toList()));
+ }
+ }
+
+ private Optional getSourceContent(Class> clazz) throws IOException {
+ String sourceFileName = getSourceFileName(clazz);
+ Optional sourceFile = findPathTo(sourceFileName);
+
+ return sourceFile.map(this::toContent);
+ }
+
+ private Optional findPathTo(String sourceFileName) throws IOException {
+ File cwd = new File(".");
+ File rootOfProject = findRootOfProject(cwd);
+ return findSourceFile(rootOfProject, sourceFileName);
+ }
+
+ private String toContent(File file) {
+ try {
+ byte[] encoded = Files.readAllBytes(Paths.get(file.toURI()));
+ return StandardCharsets.UTF_8.decode(ByteBuffer.wrap(encoded)).toString();
+ } catch (IOException e) {
+ throw new RuntimeException("Could not read Java source file.", e);
+ }
+ }
+
+ private Optional findSourceFile(File rootOfProject, String sourceFileName) throws IOException {
+ Path startingDir = Paths.get(rootOfProject.toURI());
+ return Files.find(startingDir, 15, (path, attrs) -> path.endsWith(sourceFileName))
+ .map(p -> new File(p.toUri()))
+ .findFirst();
+ }
+
+ private File findRootOfProject(File cwd) {
+ File[] pomFiles = cwd.listFiles((file, name) -> { return name.equals("pom.xml"); });
+ if (pomFiles != null && pomFiles.length == 1) {
+ return cwd;
+ } else if (cwd.getParentFile() == null) {
+ throw new RuntimeException("Couldn't find directory containing pom.xml. Last looked in: " + cwd.getAbsolutePath());
+ } else {
+ return findRootOfProject(cwd.getParentFile());
+ }
+ }
+
+ private String getSourceFileName(Class> clazz) throws IOException {
+ String resourceName = clazz.getName().replace(".", "/").concat(".class");
+ ClassReader reader = new ClassReader(clazz.getClassLoader().getResourceAsStream(resourceName));
+ SourceFileNameVisitor sourceFileNameVisitor = new SourceFileNameVisitor();
+ reader.accept(sourceFileNameVisitor, 0);
+
+ return sourceFileNameVisitor.getSourceFile();
+ }
+
+
+ private static final class SourceFileNameVisitor extends ClassVisitor {
+
+ private String sourceFile = null;
+ private boolean visitedYet = false;
+
+ public SourceFileNameVisitor() {
+ super(Opcodes.ASM5);
+ }
+
+ @Override
+ public void visitSource(String source, String debug) {
+ this.visitedYet = true;
+ this.sourceFile = source;
+ super.visitSource(source, debug);
+ }
+
+ public String getSourceFile() {
+ if (!visitedYet) throw new IllegalStateException("Must visit a class before asking for source file");
+ return this.sourceFile;
+ }
+ }
+
+}
From 016a43670ca98fdf853b055343de60edff9ed0fb Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Fri, 4 Oct 2013 01:00:49 +0100
Subject: [PATCH 38/61] Add license headers.
---
.../lambda/tutorial/exercise3/Books.java | 8 ++--
.../lambda/tutorial/exercise4/Document.java | 22 +++++++++++
.../lambda/tutorial/exercise4/Printers.java | 38 +++++++++++--------
.../lambda/tutorial/Documents.java | 10 +++--
4 files changed, 55 insertions(+), 23 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
index 4d344d3..4a83ba6 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise3/Books.java
@@ -13,15 +13,15 @@
* %%
* 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
+ * 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
+ *
+ * You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
index fda540f..a79d97b 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
@@ -1,5 +1,27 @@
package org.adoptopenjdk.lambda.tutorial.exercise4;
+/*
+ * #%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
+ * .
+ * #L%
+ */
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java
index 4bd6d76..1f394ad 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java
@@ -1,5 +1,27 @@
package org.adoptopenjdk.lambda.tutorial.exercise4;
+/*
+ * #%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
+ * .
+ * #L%
+ */
+
import java.util.Arrays;
import java.util.List;
import static java.util.stream.Collectors.toList;
@@ -19,20 +41,4 @@ public static void printPages(Document doc, Integer... pageNumbers) {
public static Stream createPagesFrom(Stream contents) {
return contents.map(Page::new);
}
-
-
- public static void main(String... args) {
- Page p1 = new Page("this is the first page");
- Page p2 = new Page("this is the second page");
-
- Document myDocument = new Document(Arrays.asList(p1, p2));
-
- Printers.printPages(myDocument, 0, 1);
-
- List pages = Arrays.asList(p1, p2);
- pages.stream().map(Page::getContent).forEach(Printers::print);
-
- Stream pagesFromContent = createPagesFrom(Arrays.asList("a", "b").stream());
- System.out.println(pagesFromContent.collect(toList()));
- }
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
index af52656..48792dc 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
@@ -24,6 +24,7 @@
import org.adoptopenjdk.lambda.tutorial.exercise4.Document;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -35,8 +36,11 @@ public class Documents {
* Return the titles from a list of documents.
*/
public static List titlesOf(Document... documents) {
- return Arrays.stream(documents)
- .map(d -> d.getTitle())
- .collect(toList());
+ // No equivalent in pre-Java 8
+ List titles = new ArrayList<>();
+ for (Document doc: documents) {
+ titles.add(doc.getTitle()); // Document::getTitle
+ }
+ return titles;
}
}
From 36e3816820115a8087882e5dcc0b908e5b6b2b77 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Fri, 4 Oct 2013 18:24:04 +0100
Subject: [PATCH 39/61] Make the name of test consistent with the category of
method reference as described in introduction.
---
.../java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
index 931bf67..06ad9ee 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
@@ -156,7 +156,7 @@ public class Exercise_4_Test {
*
*/
@Test
- public void getListOfDocumentTitlesUsingInstanceMethodReference() {
+ public void getListOfDocumentTitlesUsingReferenceOfInstanceMethodBelongingToAClass() {
Document expenses = new Document("My Expenses",
Arrays.asList(new Page("LJC Open Conference ticket: £25"), new Page("Beer stipend: £100")));
Document toDoList = new Document("My ToDo List",
From 09fec08c8e5033d76494b3fd4770273efe88745c Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Fri, 4 Oct 2013 18:48:16 +0100
Subject: [PATCH 40/61] Add an example for a reference to a static method.
Conflicts:
src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java
---
.../lambda/tutorial/exercise4/Document.java | 4 +-
.../lambda/tutorial/exercise4/Printers.java | 44 -------------------
.../lambda/tutorial/Documents.java | 9 ++++
.../lambda/tutorial/Exercise_4_Test.java | 25 ++++++++++-
4 files changed, 34 insertions(+), 48 deletions(-)
delete mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
index a79d97b..f6c4b9f 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
@@ -35,8 +35,8 @@ public Document(String title, List pages) {
this.pages = Collections.unmodifiableList(new ArrayList<>(pages));
}
- public String getPageContent(Integer pageNumber) {
- return this.pages.get(pageNumber).getContent();
+ public List getPages() {
+ return this.pages;
}
public String getTitle() {
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java
deleted file mode 100644
index 1f394ad..0000000
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Printers.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.adoptopenjdk.lambda.tutorial.exercise4;
-
-/*
- * #%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
- * .
- * #L%
- */
-
-import java.util.Arrays;
-import java.util.List;
-import static java.util.stream.Collectors.toList;
-import java.util.stream.Stream;
-
-import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
-
-public class Printers {
- public static void print(String s) {
- System.out.println(s);
- }
-
- public static void printPages(Document doc, Integer... pageNumbers) {
- Arrays.stream(pageNumbers).map(doc::getPageContent).forEach(Printers::print);
- }
-
- public static Stream createPagesFrom(Stream contents) {
- return contents.map(Page::new);
- }
-}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
index 48792dc..f1963a0 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
@@ -23,6 +23,7 @@
*/
import org.adoptopenjdk.lambda.tutorial.exercise4.Document;
+import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
import java.util.ArrayList;
import java.util.Arrays;
@@ -43,4 +44,12 @@ public static List titlesOf(Document... documents) {
}
return titles;
}
+
+ public static Integer characterCount(Page page) {
+ return page.getContent().length();
+ }
+
+ public static List pageCharacterCounts(Document document) {
+ return document.getPages().stream().map(doc -> Documents.characterCount(doc)).collect(toList());
+ }
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
index 06ad9ee..b96eb7e 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
@@ -24,7 +24,7 @@
import org.adoptopenjdk.lambda.tutorial.exercise4.Document;
import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
-import org.hamcrest.Matchers;
+import static org.hamcrest.Matchers.*;
import org.junit.Test;
import java.util.Arrays;
@@ -165,9 +165,30 @@ public void getListOfDocumentTitlesUsingReferenceOfInstanceMethodBelongingToACla
Arrays.asList(new Page("Oracle Certified Professional"), new Page("Swimming 10m")));
assertThat(Documents.titlesOf(expenses, toDoList, certificates),
- Matchers.contains("My Expenses", "My ToDo List", "My Certificates"));
+ contains("My Expenses", "My ToDo List", "My Certificates"));
assertThat(Documents.class, usesMethodReferences("getTitle"));
}
+ /**
+ * The Documents class has a method which calculates a list of the character counts of Pages in a
+ * Document. The method characterCount can be applied to each Page to calculate the number of
+ * characters in that page. Currently it is invoked using a lambda.
+ *
+ * Change to use a method reference which uses the static characterCount method.
+ *
+ * @see Documents#pageCharacterCounts(Document)
+ * @see Documents#characterCount(Page)
+ */
+ @Test
+ public void getListOfPageCharacterCountsFromDocumentUsingReferenceOfStaticMethodBelongingToAClass() {
+ Document diary = new Document("My Diary", Arrays.asList(
+ new Page("Today I went shopping"),
+ new Page("Today I did maths"),
+ new Page("Today I wrote in my diary")));
+
+ assertThat(Documents.pageCharacterCounts(diary), contains(21, 17, 25));
+ assertThat(Documents.class, usesMethodReferences("characterCount"));
+ }
+
}
From 039bf71841e07f3971736dad353f32711225a9ec Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Fri, 4 Oct 2013 18:53:07 +0100
Subject: [PATCH 41/61] Show pre-java 8 solution.
---
.../java/org/adoptopenjdk/lambda/tutorial/Documents.java | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
index f1963a0..7984ac1 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
@@ -50,6 +50,11 @@ public static Integer characterCount(Page page) {
}
public static List pageCharacterCounts(Document document) {
- return document.getPages().stream().map(doc -> Documents.characterCount(doc)).collect(toList());
+ // No equivalent in pre-Java 8
+ List characterCounts = new ArrayList<>();
+ for (Page page: document.getPages()) {
+ characterCounts.add(Documents.characterCount(page)); // Documents::characterCount
+ }
+ return characterCounts;
}
}
From 67b75399b6a7e818a1a67197fa2020e908be5c5e Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Fri, 4 Oct 2013 19:34:20 +0100
Subject: [PATCH 42/61] Add test case for using method references to invoke
methods on a particular instance.
---
.../tutorial/exercise4/PagePrinter.java | 48 ++++++++++++++++
.../lambda/tutorial/Documents.java | 13 +++++
.../lambda/tutorial/Exercise_4_Test.java | 37 +++++++++++++
.../util/StringWithComparisonMatcher.java | 55 +++++++++++++++++++
4 files changed, 153 insertions(+)
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/PagePrinter.java
create mode 100644 src/test/java/org/adoptopenjdk/lambda/tutorial/util/StringWithComparisonMatcher.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/PagePrinter.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/PagePrinter.java
new file mode 100644
index 0000000..503692a
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/PagePrinter.java
@@ -0,0 +1,48 @@
+package org.adoptopenjdk.lambda.tutorial.exercise4;
+
+/*
+ * #%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
+ * .
+ * #L%
+ */
+
+import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
+
+import static java.lang.String.format;
+
+public final class PagePrinter {
+
+ private final String pageBreak;
+
+ public PagePrinter(String pageBreak) {
+ this.pageBreak = pageBreak;
+ }
+
+ public String printTitlePage(Document document) {
+ return format(
+ "%s%n" +
+ "%s%n", document.getTitle(), pageBreak);
+ }
+
+ public String printPage(Page page) {
+ return format(
+ "%s%n" +
+ "%s%n", page.getContent(), pageBreak);
+ }
+}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
index 7984ac1..7d7d5a2 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
@@ -24,11 +24,13 @@
import org.adoptopenjdk.lambda.tutorial.exercise4.Document;
import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
+import org.adoptopenjdk.lambda.tutorial.exercise4.PagePrinter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
public class Documents {
@@ -57,4 +59,15 @@ public static List pageCharacterCounts(Document document) {
}
return characterCounts;
}
+
+ public static String print(Document document, PagePrinter pagePrinter) {
+ StringBuilder output = new StringBuilder();
+
+ output.append(pagePrinter.printTitlePage(document));
+ document.getPages().stream()
+ .map(p -> pagePrinter.printPage(p))
+ .forEach(s -> output.append(s));
+
+ return output.toString();
+ }
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
index b96eb7e..58de542 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
@@ -24,7 +24,11 @@
import org.adoptopenjdk.lambda.tutorial.exercise4.Document;
import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
+
+import static java.lang.String.format;
import static org.hamcrest.Matchers.*;
+
+import org.adoptopenjdk.lambda.tutorial.exercise4.PagePrinter;
import org.junit.Test;
import java.util.Arrays;
@@ -32,6 +36,7 @@
import static org.adoptopenjdk.lambda.tutorial.util.CodeUsesMethodReferencesMatcher.usesMethodReferences;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.adoptopenjdk.lambda.tutorial.util.StringWithComparisonMatcher.*;
/**
* Exercise 4 - Method References
@@ -191,4 +196,36 @@ public void getListOfPageCharacterCountsFromDocumentUsingReferenceOfStaticMethod
assertThat(Documents.class, usesMethodReferences("characterCount"));
}
+ /**
+ * The Documents class has a method which takes a PagePrinter and renders a
+ * Document to text. The method uses two lambda expressions where method references can be used. In
+ * this case the method references are to methods belonging to a particular instance object.
+ *
+ * Change {@link Documents#print(Document, PagePrinter)} to use method references to invoke instance methods of
+ * particular objects.
+ *
+ * @see Documents#print(Document, PagePrinter)
+ * @see StringBuilder#append
+ * @see PagePrinter#printPage(Page)
+ *
+ */
+ @Test
+ public void printContentsOfDocumentUsingReferenceOfInstanceMethodBeloningToAnObject() {
+ Document diary = new Document("My Diary", Arrays.asList(
+ new Page("Today I went shopping"),
+ new Page("Today I did maths"),
+ new Page("Today I wrote in my diary")));
+
+ assertThat(Documents.print(diary, new PagePrinter("----")),
+ isString(format("My Diary%n" +
+ "----%n" +
+ "Today I went shopping%n" +
+ "----%n" +
+ "Today I did maths%n" +
+ "----%n" +
+ "Today I wrote in my diary%n" +
+ "----%n")));
+ assertThat(Documents.class, allOf(usesMethodReferences("printPage"), usesMethodReferences("append")));
+ }
+
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/util/StringWithComparisonMatcher.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/util/StringWithComparisonMatcher.java
new file mode 100644
index 0000000..dc661d0
--- /dev/null
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/util/StringWithComparisonMatcher.java
@@ -0,0 +1,55 @@
+package org.adoptopenjdk.lambda.tutorial.util;
+
+/*
+ * #%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
+ * .
+ * #L%
+ */
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.junit.ComparisonFailure;
+
+public class StringWithComparisonMatcher extends TypeSafeDiagnosingMatcher {
+ private final String expected;
+
+ private StringWithComparisonMatcher(String expected) {
+ this.expected = expected;
+ }
+
+ public static StringWithComparisonMatcher isString(String expected) {
+ return new StringWithComparisonMatcher(expected);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(expected);
+ }
+
+ @Override
+ protected boolean matchesSafely(String actual, Description mismatchDescription) {
+ if (!expected.equals(actual)) {
+ String compactedMismatch = new ComparisonFailure("did not match:", expected, actual).getMessage();
+ mismatchDescription.appendText(compactedMismatch);
+ return false;
+ }
+ return true;
+ }
+
+}
\ No newline at end of file
From c470f56d0c48beb1d18af6f5a6c1757d5085dc1a Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Fri, 4 Oct 2013 19:46:35 +0100
Subject: [PATCH 43/61] Add passing test case for pre-java 8.
---
.../java/org/adoptopenjdk/lambda/tutorial/Documents.java | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
index 7d7d5a2..ee0b25c 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
@@ -61,12 +61,15 @@ public static List pageCharacterCounts(Document document) {
}
public static String print(Document document, PagePrinter pagePrinter) {
+ // No equivalent in pre-Java 8
StringBuilder output = new StringBuilder();
output.append(pagePrinter.printTitlePage(document));
- document.getPages().stream()
- .map(p -> pagePrinter.printPage(p))
- .forEach(s -> output.append(s));
+
+ for (Page page: document.getPages()) {
+ String content = pagePrinter.printPage(page); // PagePrinter::printPage
+ output.append(content); // StringBuilder::append
+ }
return output.toString();
}
From 3ac42ed546067662f0e97bc4072e7c4e8043f7b4 Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sun, 6 Oct 2013 21:48:23 +0100
Subject: [PATCH 44/61] Add test case to show how to use method reference using
'this'.
---
.../lambda/tutorial/exercise4/Document.java | 19 ++++++++++++
.../lambda/tutorial/Exercise_4_Test.java | 31 +++++++++++++++++++
2 files changed, 50 insertions(+)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
index f6c4b9f..56976b5 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
@@ -25,6 +25,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+import static java.util.stream.Collectors.collectingAndThen;
public final class Document {
private final String title;
@@ -43,6 +47,21 @@ public String getTitle() {
return this.title;
}
+ private Page appendFooter(Page original) {
+ String footer = "Document: " + getTitle();
+ return new Page(format("%s%n%s", original.getContent(), footer));
+ }
+
+ private Document copyWithPages(List newPages) {
+ return new Document(title, newPages);
+ }
+
+ public Document copyWithFooter() {
+ return getPages().stream()
+ .map(page -> appendFooter(page))
+ .collect(collectingAndThen(Collectors.toList(), pages -> copyWithPages(pages)));
+ }
+
public static final class Page {
private final String content;
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
index 58de542..2a1bc97 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
@@ -29,6 +29,8 @@
import static org.hamcrest.Matchers.*;
import org.adoptopenjdk.lambda.tutorial.exercise4.PagePrinter;
+import org.adoptopenjdk.lambda.tutorial.util.FeatureMatchers;
+import org.hamcrest.Matcher;
import org.junit.Test;
import java.util.Arrays;
@@ -228,4 +230,33 @@ public void printContentsOfDocumentUsingReferenceOfInstanceMethodBeloningToAnObj
assertThat(Documents.class, allOf(usesMethodReferences("printPage"), usesMethodReferences("append")));
}
+
+ /**
+ * The Document class has a method which can create a new Document where all the pages have had a
+ * footer appended to it of the format "Document: {title}". The method uses two lambda expressions where method
+ * references can be used. In this case the method references are to methods belonging to this object
+ * instance. That is, the methods to be invoked should be invoked on this.
+ *
+ * Change {@link Document#copyWithFooter()} to use method references to invoke instance methods on this
+ * instance.
+ */
+ @Test
+ public void transformPagesToHaveFooterUsingReferenceOfInstanceMethodBelonginToThisObject() {
+ Page blankPage = new Page("");
+ Document diary = new Document("My Diary", Arrays.asList(
+ new Page("Today I went shopping"),
+ blankPage,
+ new Page("Today I did maths"),
+ blankPage,
+ new Page("Today I wrote in my diary")));
+
+ Document diaryWithFooters = diary.copyWithFooter();
+
+ assertThat(diaryWithFooters.getPages(), everyItem(pageEndingWith("Document: My Diary")));
+ assertThat(Document.class, allOf(usesMethodReferences("appendFooter"), usesMethodReferences("copyWithPages")));
+ }
+
+ private static Matcher pageEndingWith(String ending) {
+ return FeatureMatchers.from(endsWith(ending), "page containing", "contents", Page::getContent);
+ }
}
From f593568c040119d6590c4ac4b0081f47a74a3828 Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sun, 6 Oct 2013 21:59:58 +0100
Subject: [PATCH 45/61] Add (non) equivalent in pre java 8.
---
.../adoptopenjdk/lambda/tutorial/exercise4/Document.java | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
index 56976b5..ba1464d 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
@@ -57,9 +57,12 @@ private Document copyWithPages(List newPages) {
}
public Document copyWithFooter() {
- return getPages().stream()
- .map(page -> appendFooter(page))
- .collect(collectingAndThen(Collectors.toList(), pages -> copyWithPages(pages)));
+ // No equivalent in pre-Java 8
+ List pagesWithFooter = new ArrayList<>();
+ for (Page page: getPages()) {
+ pagesWithFooter.add(appendFooter(page)); // this::appendFooter
+ }
+ return copyWithPages(pagesWithFooter); // this::copyWithPages
}
public static final class Page {
From 806c523a7d7f2851e30fbcc190c834295c4672b9 Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Sun, 6 Oct 2013 22:04:11 +0100
Subject: [PATCH 46/61] Whoops, forgot to remove blankPage variable from when
figuring out what this test should do.
---
.../java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
index 2a1bc97..3a6906b 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
@@ -242,12 +242,9 @@ public void printContentsOfDocumentUsingReferenceOfInstanceMethodBeloningToAnObj
*/
@Test
public void transformPagesToHaveFooterUsingReferenceOfInstanceMethodBelonginToThisObject() {
- Page blankPage = new Page("");
Document diary = new Document("My Diary", Arrays.asList(
new Page("Today I went shopping"),
- blankPage,
new Page("Today I did maths"),
- blankPage,
new Page("Today I wrote in my diary")));
Document diaryWithFooters = diary.copyWithFooter();
From f6cf03b593f35803404843b2c9aef566ca55e717 Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Mon, 7 Oct 2013 21:13:41 +0100
Subject: [PATCH 47/61] Add correctly failing test for using constructor method
reference.
---
.../lambda/tutorial/exercise4/Translator.java | 19 ++++++++++++++++
.../lambda/tutorial/Documents.java | 10 +++++++++
.../lambda/tutorial/Exercise_4_Test.java | 22 +++++++++++++++++++
3 files changed, 51 insertions(+)
create mode 100644 src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Translator.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Translator.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Translator.java
new file mode 100644
index 0000000..61ef0fd
--- /dev/null
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Translator.java
@@ -0,0 +1,19 @@
+package org.adoptopenjdk.lambda.tutorial.exercise4;
+
+public interface Translator {
+
+ String translate(String input);
+
+ enum Languages implements Translator {
+ REVERSISH {
+ @Override
+ public String translate(String input) {
+ return new StringBuilder(input).reverse().toString();
+ }
+ }
+
+ // TODO: implement other, real languages.
+ }
+
+}
+
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
index ee0b25c..4a279f5 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
@@ -25,12 +25,14 @@
import org.adoptopenjdk.lambda.tutorial.exercise4.Document;
import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
import org.adoptopenjdk.lambda.tutorial.exercise4.PagePrinter;
+import org.adoptopenjdk.lambda.tutorial.exercise4.Translator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static java.lang.String.format;
+import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
public class Documents {
@@ -73,4 +75,12 @@ public static String print(Document document, PagePrinter pagePrinter) {
return output.toString();
}
+
+ public static Document translate(Document document, Translator translator) {
+ return document.getPages().stream()
+ .map(page -> page.getContent())
+ .map(content -> translator.translate(content))
+ .map(translated -> new Page(translated))
+ .collect(collectingAndThen(toList(), pages -> new Document(document.getTitle(), pages)));
+ }
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
index 3a6906b..c8f649c 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
@@ -29,6 +29,7 @@
import static org.hamcrest.Matchers.*;
import org.adoptopenjdk.lambda.tutorial.exercise4.PagePrinter;
+import org.adoptopenjdk.lambda.tutorial.exercise4.Translator.Languages;
import org.adoptopenjdk.lambda.tutorial.util.FeatureMatchers;
import org.hamcrest.Matcher;
import org.junit.Test;
@@ -253,7 +254,28 @@ public void transformPagesToHaveFooterUsingReferenceOfInstanceMethodBelonginToTh
assertThat(Document.class, allOf(usesMethodReferences("appendFooter"), usesMethodReferences("copyWithPages")));
}
+
+ @Test
+ public void createNewDocumentWithTranslatedPagesUsingReferenceOfConstructorMethod() {
+ Document diary = new Document("My Diary", Arrays.asList(
+ new Page("Today I went shopping"),
+ new Page("Today I did maths"),
+ new Page("Today I wrote in my diary")));
+
+ Document translated = Documents.translate(diary, Languages.REVERSISH);
+
+ assertThat(translated.getPages(),
+ contains(pageContaining("gnippohs tnew I yadoT"),
+ pageContaining("shtam did I yadoT"),
+ pageContaining("yraid ym ni etorw I yadoT")));
+ assertThat(Documents.class, usesMethodReferences("new"));
+ }
+
private static Matcher pageEndingWith(String ending) {
return FeatureMatchers.from(endsWith(ending), "page containing", "contents", Page::getContent);
}
+
+ private static Matcher pageContaining(String content) {
+ return FeatureMatchers.from(isString(content), "page containing", "contents", Page::getContent);
+ }
}
From 28ac65c10f0a02d14ec0a8245b6a7c59fa38b4d9 Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Mon, 7 Oct 2013 22:33:04 +0100
Subject: [PATCH 48/61] Finish javadoc for final test of method references
chapter.
---
.../lambda/tutorial/Documents.java | 3 ++-
.../lambda/tutorial/Exercise_4_Test.java | 23 +++++++++++++++----
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
index 4a279f5..118dad3 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
@@ -81,6 +81,7 @@ public static Document translate(Document document, Translator translator) {
.map(page -> page.getContent())
.map(content -> translator.translate(content))
.map(translated -> new Page(translated))
- .collect(collectingAndThen(toList(), pages -> new Document(document.getTitle(), pages)));
+ .collect(collectingAndThen(toList(),
+ pages -> new Document(translator.translate(document.getTitle()), pages)));
}
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
index c8f649c..85ff9f6 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
@@ -24,11 +24,8 @@
import org.adoptopenjdk.lambda.tutorial.exercise4.Document;
import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
-
-import static java.lang.String.format;
-import static org.hamcrest.Matchers.*;
-
import org.adoptopenjdk.lambda.tutorial.exercise4.PagePrinter;
+import org.adoptopenjdk.lambda.tutorial.exercise4.Translator;
import org.adoptopenjdk.lambda.tutorial.exercise4.Translator.Languages;
import org.adoptopenjdk.lambda.tutorial.util.FeatureMatchers;
import org.hamcrest.Matcher;
@@ -37,9 +34,14 @@
import java.util.Arrays;
import java.util.function.Consumer;
+import static java.lang.String.format;
import static org.adoptopenjdk.lambda.tutorial.util.CodeUsesMethodReferencesMatcher.usesMethodReferences;
+import static org.adoptopenjdk.lambda.tutorial.util.StringWithComparisonMatcher.isString;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.adoptopenjdk.lambda.tutorial.util.StringWithComparisonMatcher.*;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.everyItem;
/**
* Exercise 4 - Method References
@@ -255,6 +257,17 @@ public void transformPagesToHaveFooterUsingReferenceOfInstanceMethodBelonginToTh
}
+ /**
+ * The Documents class has a method which can translate a document into another language. The method
+ * uses a lambda expression to construct each translated Page, where it could use a method reference
+ * to Page's constructor.
+ *
+ * Change {@link Documents#translate} to use a method reference to construct each translated Page.
+ *
+ * @see Documents#translate(Document, Translator)
+ * @see Translator.Languages
+ * @see Page
+ */
@Test
public void createNewDocumentWithTranslatedPagesUsingReferenceOfConstructorMethod() {
Document diary = new Document("My Diary", Arrays.asList(
From 680efa27895405911c4469347101afc33b33430c Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Mon, 7 Oct 2013 22:39:20 +0100
Subject: [PATCH 49/61] Show (non) equivalent of constructor method refs.
---
.../adoptopenjdk/lambda/tutorial/Documents.java | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
index 118dad3..71b8890 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
@@ -77,11 +77,16 @@ public static String print(Document document, PagePrinter pagePrinter) {
}
public static Document translate(Document document, Translator translator) {
- return document.getPages().stream()
- .map(page -> page.getContent())
- .map(content -> translator.translate(content))
- .map(translated -> new Page(translated))
- .collect(collectingAndThen(toList(),
- pages -> new Document(translator.translate(document.getTitle()), pages)));
+ // No equivalent in pre-Java 8
+
+ List translatedPages = new ArrayList<>();
+ for (Page page: document.getPages()) {
+ String content = page.getContent();
+ String translatedContent = translator.translate(content);
+ Page translatedPage = new Page(translatedContent); // Page::new
+ translatedPages.add(translatedPage);
+ }
+
+ return new Document(translator.translate(document.getTitle()), translatedPages);
}
}
From 935247b3708531c34aa8000a73ffe36f52138432 Mon Sep 17 00:00:00 2001
From: Grundlefleck
Date: Mon, 7 Oct 2013 22:42:13 +0100
Subject: [PATCH 50/61] Merge README from master.
---
README.md | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/README.md b/README.md
index c8123ae..6742287 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,7 @@ To follow the exercises:
1. Internal vs External Iteration (the forEach method)
2. Filtering and Collecting
3. Mapping
+ 4. (In Progress) Method References
[More to come]
@@ -34,3 +35,11 @@ The current tutorial is known to work with the following JDK build:
|ea b109 |Sep 26, 2013|
lambda-tutorial will try to track against the newest version available. If you find that you are working with a newer version of the Lambda JDK and the tutorial does not compile or run, please file an issue.
+
+### IDE Setup
+- [IntelliJ IDEA on Ubuntu](https://github.com/AdoptOpenJDK/lambda-tutorial/wiki/IntelliJ-IDEA-on-Ubuntu-%5BLinux%5D)
+- [IntelliJ IDEA on MacOS](https://github.com/AdoptOpenJDK/lambda-tutorial/wiki/IntelliJ-IDEA-on-MacOS)
+- [IntelliJ IDEA deutsche Anleitung (u.a. Windows)](https://github.com/AdoptOpenJDK/lambda-tutorial/wiki/IntelliJ-IDEA-Einrichtung)
+- [Eclipse Kepler 4.3 on Windows](https://github.com/AdoptOpenJDK/lambda-tutorial/wiki/Eclipse-Lambda-EA-Setup)
+
+Note: we are hoping the instructions are not too sensitive to the OSes on which they have been performed.
From e2f7d54b01a7411202a4174afafe4774184a7a77 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Sun, 13 Oct 2013 21:04:38 +0100
Subject: [PATCH 51/61] Move Documents class into the appropriate package and
source folder.
---
.../lambda/tutorial/exercise4}/Documents.java | 8 +------
.../lambda/tutorial/exercise4/Translator.java | 22 +++++++++++++++++++
.../lambda/tutorial/Exercise_4_Test.java | 1 +
3 files changed, 24 insertions(+), 7 deletions(-)
rename src/{test/java/org/adoptopenjdk/lambda/tutorial => main/java/org/adoptopenjdk/lambda/tutorial/exercise4}/Documents.java (88%)
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Documents.java
similarity index 88%
rename from src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
rename to src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Documents.java
index 71b8890..b4fa38a 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Documents.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Documents.java
@@ -1,4 +1,4 @@
-package org.adoptopenjdk.lambda.tutorial;
+package org.adoptopenjdk.lambda.tutorial.exercise4;
/*
* #%L
@@ -22,18 +22,12 @@
* #L%
*/
-import org.adoptopenjdk.lambda.tutorial.exercise4.Document;
import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
-import org.adoptopenjdk.lambda.tutorial.exercise4.PagePrinter;
-import org.adoptopenjdk.lambda.tutorial.exercise4.Translator;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import static java.lang.String.format;
-import static java.util.stream.Collectors.collectingAndThen;
-import static java.util.stream.Collectors.toList;
public class Documents {
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Translator.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Translator.java
index 61ef0fd..7d3a700 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Translator.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Translator.java
@@ -1,5 +1,27 @@
package org.adoptopenjdk.lambda.tutorial.exercise4;
+/*
+ * #%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
+ * .
+ * #L%
+ */
+
public interface Translator {
String translate(String input);
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
index 85ff9f6..1bb2ffc 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
@@ -24,6 +24,7 @@
import org.adoptopenjdk.lambda.tutorial.exercise4.Document;
import org.adoptopenjdk.lambda.tutorial.exercise4.Document.Page;
+import org.adoptopenjdk.lambda.tutorial.exercise4.Documents;
import org.adoptopenjdk.lambda.tutorial.exercise4.PagePrinter;
import org.adoptopenjdk.lambda.tutorial.exercise4.Translator;
import org.adoptopenjdk.lambda.tutorial.exercise4.Translator.Languages;
From 8efe2c66ec761e1955299fd204d009e74398ee9d Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Sat, 19 Oct 2013 12:44:12 +0100
Subject: [PATCH 52/61] Method References chapter is now considered complete.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 6742287..48fd16a 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ To follow the exercises:
1. Internal vs External Iteration (the forEach method)
2. Filtering and Collecting
3. Mapping
- 4. (In Progress) Method References
+ 4. Method References
[More to come]
From 19ea12fbcf79fbf08d1004fef8a08de6f230f5eb Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Sun, 16 Feb 2014 12:49:08 +0000
Subject: [PATCH 53/61] Add Travis CI builds to pre-jdk8 tests.
---
.travis.yml | 6 ++++++
README.md | 4 +++-
2 files changed, 9 insertions(+), 1 deletion(-)
create mode 100644 .travis.yml
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6c23cde
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,6 @@
+language: java
+jdk:
+ - oraclejdk7
+ - openjdk7
+script: "mvn clean test"
+
diff --git a/README.md b/README.md
index 48fd16a..d1425a6 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
-## Lambda Tutorial
+## Lambda Tutorial
+[](https://travis-ci.org/AdoptOpenJDK/lambda-tutorial)
A set of exercises to teach use of Java 8 lambda syntax, and the new Streams API.
@@ -43,3 +44,4 @@ lambda-tutorial will try to track against the newest version available. If you f
- [Eclipse Kepler 4.3 on Windows](https://github.com/AdoptOpenJDK/lambda-tutorial/wiki/Eclipse-Lambda-EA-Setup)
Note: we are hoping the instructions are not too sensitive to the OSes on which they have been performed.
+
From 33737e4e65d9b7b17b4b82a84ed35e5d12ca3803 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Sun, 16 Feb 2014 13:01:00 +0000
Subject: [PATCH 54/61] Retrieve build status icon for the correct branch.
---
README.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/README.md b/README.md
index d1425a6..00397b6 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-## Lambda Tutorial
-[](https://travis-ci.org/AdoptOpenJDK/lambda-tutorial)
+## Lambda Tutorial [](https://travis-ci.org/AdoptOpenJDK/lambda-tutorial)
A set of exercises to teach use of Java 8 lambda syntax, and the new Streams API.
From 568921d3b4f3fe4beaaf68ebbbf7920f6cf6b844 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Sun, 16 Feb 2014 13:10:11 +0000
Subject: [PATCH 55/61] Correct jdk version used.
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 688d5cc..046741e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,7 +64,7 @@
UTF-8
- 1.8
+ 1.7
From 837a8dcc09192495e3aa97878de9bf0a00caa0fa Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Sun, 16 Feb 2014 14:04:55 +0000
Subject: [PATCH 56/61] Allow tests to pass against JDK 7. Involves removing
all references to JDK 8 lib classes and syntax.
---
.../ConfigureYourLambdaBuildOfJdk.java | 4 +-
.../tutorial/exercise2/ElectoralDistrict.java | 1 -
.../lambda/tutorial/exercise4/Document.java | 2 -
.../lambda/tutorial/Exercise_1_Test.java | 6 +-
.../lambda/tutorial/Exercise_2_Test.java | 23 ++-
.../lambda/tutorial/Exercise_3_Test.java | 10 +-
.../lambda/tutorial/Exercise_4_Test.java | 20 ++-
.../util/CodeUsesMethodReferencesMatcher.java | 169 ------------------
.../lambda/tutorial/util/FeatureMatchers.java | 13 +-
9 files changed, 47 insertions(+), 201 deletions(-)
delete mode 100644 src/test/java/org/adoptopenjdk/lambda/tutorial/util/CodeUsesMethodReferencesMatcher.java
diff --git a/src/main/java/org/adoptopenjdk/lambda/setupcheck/ConfigureYourLambdaBuildOfJdk.java b/src/main/java/org/adoptopenjdk/lambda/setupcheck/ConfigureYourLambdaBuildOfJdk.java
index 8c87c4a..2385664 100644
--- a/src/main/java/org/adoptopenjdk/lambda/setupcheck/ConfigureYourLambdaBuildOfJdk.java
+++ b/src/main/java/org/adoptopenjdk/lambda/setupcheck/ConfigureYourLambdaBuildOfJdk.java
@@ -51,6 +51,8 @@ public static void main(String... args) {
"",
"Until this source file compiles, you will be unable to make progress in the tutorial.");
- messages.forEach(System.out::println);
+ // This is not expected to compile with JDK 7.
+ // (for pre-JDK8 solutions examples only)
+ // messages.forEach(System.out::println);
}
}
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
index 9628499..769b08c 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise2/ElectoralDistrict.java
@@ -26,7 +26,6 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
-import java.util.stream.Collectors;
/**
* Some (inaccurate) London electrical districts
diff --git a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
index ba1464d..82c8986 100644
--- a/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
+++ b/src/main/java/org/adoptopenjdk/lambda/tutorial/exercise4/Document.java
@@ -25,10 +25,8 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.stream.Collectors;
import static java.lang.String.format;
-import static java.util.stream.Collectors.collectingAndThen;
public final class Document {
private final String title;
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java
index 4a4a0b9..00ea7f4 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_1_Test.java
@@ -165,6 +165,10 @@ public void changeColorOfAllShapes_AND_buildStringShowingAllTheOldColors() {
// ----- Test helpers -----
private static Matcher hasColor(Color color) {
- return FeatureMatchers.from(Matchers.is(color), "has color", "color", Shape::getColor);
+ return FeatureMatchers.from(Matchers.is(color), "has color", "color", new FeatureMatchers.Extractor() {
+ @Override public Color get(Shape shape) {
+ return shape.getColor();
+ }
+ });
}
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
index 4803a9e..eee3c02 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_2_Test.java
@@ -38,11 +38,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.stream.Collector;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import static java.util.Arrays.asList;
import static java.util.Arrays.binarySearch;
@@ -268,15 +263,27 @@ public void getAllPersonsEligibleToVote() {
// Test helpers
private static Matcher aPersonNamed(String name) {
- return FeatureMatchers.from(is(name), "a person", "name", person -> person.getName());
+ return FeatureMatchers.from(is(name), "a person", "name", new FeatureMatchers.Extractor() {
+ @Override public String get(Person person) {
+ return person.getName();
+ }
+ });
}
private static Matcher aVoterWithId(String name) {
- return FeatureMatchers.from(is(name), "a voter", "electorId", voter -> voter.getElectorId());
+ return FeatureMatchers.from(is(name), "a voter", "electorId", new FeatureMatchers.Extractor() {
+ @Override public String get(RegisteredVoter registeredVoter) {
+ return registeredVoter.getElectorId();
+ }
+ });
}
private static Matcher spoiled() {
- return FeatureMatchers.from(equalTo(Boolean.TRUE), "a spoiled ballot", "isSpoiled", ballot -> ballot.isSpoiled());
+ return FeatureMatchers.from(equalTo(Boolean.TRUE), "a spoiled ballot", "isSpoiled", new FeatureMatchers.Extractor() {
+ @Override public Boolean get(Ballot ballot) {
+ return ballot.isSpoiled();
+ }
+ });
}
}
\ No newline at end of file
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
index df14c3a..8ebaff3 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_3_Test.java
@@ -33,10 +33,6 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
-import java.util.function.Function;
-import java.util.stream.Collector;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
@@ -185,7 +181,11 @@ public void getPublishersRepresentedByBooks() {
// Test helpers
private static Matcher publisherNamed(String name) {
- return FeatureMatchers.from(equalTo(name), "is named", "name", publisher -> publisher.getName());
+ return FeatureMatchers.from(equalTo(name), "is named", "name", new FeatureMatchers.Extractor() {
+ @Override public String get(Publisher publisher) {
+ return publisher.getName();
+ }
+ });
}
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
index 1bb2ffc..9f2abe5 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_4_Test.java
@@ -33,10 +33,8 @@
import org.junit.Test;
import java.util.Arrays;
-import java.util.function.Consumer;
import static java.lang.String.format;
-import static org.adoptopenjdk.lambda.tutorial.util.CodeUsesMethodReferencesMatcher.usesMethodReferences;
import static org.adoptopenjdk.lambda.tutorial.util.StringWithComparisonMatcher.isString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
@@ -177,7 +175,6 @@ public void getListOfDocumentTitlesUsingReferenceOfInstanceMethodBelongingToACla
assertThat(Documents.titlesOf(expenses, toDoList, certificates),
contains("My Expenses", "My ToDo List", "My Certificates"));
- assertThat(Documents.class, usesMethodReferences("getTitle"));
}
@@ -199,7 +196,6 @@ public void getListOfPageCharacterCountsFromDocumentUsingReferenceOfStaticMethod
new Page("Today I wrote in my diary")));
assertThat(Documents.pageCharacterCounts(diary), contains(21, 17, 25));
- assertThat(Documents.class, usesMethodReferences("characterCount"));
}
/**
@@ -231,7 +227,6 @@ public void printContentsOfDocumentUsingReferenceOfInstanceMethodBeloningToAnObj
"----%n" +
"Today I wrote in my diary%n" +
"----%n")));
- assertThat(Documents.class, allOf(usesMethodReferences("printPage"), usesMethodReferences("append")));
}
@@ -254,7 +249,6 @@ public void transformPagesToHaveFooterUsingReferenceOfInstanceMethodBelonginToTh
Document diaryWithFooters = diary.copyWithFooter();
assertThat(diaryWithFooters.getPages(), everyItem(pageEndingWith("Document: My Diary")));
- assertThat(Document.class, allOf(usesMethodReferences("appendFooter"), usesMethodReferences("copyWithPages")));
}
@@ -282,14 +276,22 @@ public void createNewDocumentWithTranslatedPagesUsingReferenceOfConstructorMetho
contains(pageContaining("gnippohs tnew I yadoT"),
pageContaining("shtam did I yadoT"),
pageContaining("yraid ym ni etorw I yadoT")));
- assertThat(Documents.class, usesMethodReferences("new"));
}
private static Matcher pageEndingWith(String ending) {
- return FeatureMatchers.from(endsWith(ending), "page containing", "contents", Page::getContent);
+ return FeatureMatchers.from(endsWith(ending), "page containing", "contents", new FeatureMatchers.Extractor() {
+ @Override public String get(Page page) {
+ return page.getContent();
+ }
+ });
}
private static Matcher pageContaining(String content) {
- return FeatureMatchers.from(isString(content), "page containing", "contents", Page::getContent);
+ return FeatureMatchers.from(isString(content), "page containing", "contents", new FeatureMatchers.Extractor() {
+ @Override
+ public String get(Page page) {
+ return page.getContent();
+ }
+ });
}
}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/util/CodeUsesMethodReferencesMatcher.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/util/CodeUsesMethodReferencesMatcher.java
deleted file mode 100644
index 70a9ba2..0000000
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/util/CodeUsesMethodReferencesMatcher.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package org.adoptopenjdk.lambda.tutorial.util;
-
-/*
- * #%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
- * .
- * #L%
- */
-
-import org.hamcrest.Description;
-import org.hamcrest.TypeSafeDiagnosingMatcher;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.Opcodes;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Optional;
-
-import static java.util.stream.Collectors.toList;
-
-public final class CodeUsesMethodReferencesMatcher extends TypeSafeDiagnosingMatcher> {
-
- private final String methodName;
-
- private CodeUsesMethodReferencesMatcher(String methodName) {
- this.methodName = methodName;
- }
-
- public static CodeUsesMethodReferencesMatcher usesMethodReferences(String methodName) {
- return new CodeUsesMethodReferencesMatcher(methodName);
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("a source file using a method reference to invoke ").appendValue(methodName);
- }
-
-
- @Override
- protected boolean matchesSafely(Class> clazz, Description mismatchDescription) {
- try {
- Optional sourceFileContent = getSourceContent(clazz);
- return sourceFileContent.map(c -> usesMethodReference(c, mismatchDescription)).orElseGet(() -> {
- mismatchDescription.appendText("could not read source file to discover if you used method references.");
- return false;
- });
- } catch (IOException e) {
- mismatchDescription.appendText("could not read source file to discover if you used method references.");
- mismatchDescription.appendValue(e);
- return false;
- }
- }
-
- private boolean usesMethodReference(String sourceCode, Description mismatchDescription) {
- if (sourceCode.contains("::"+methodName)) {
- return true;
- } else {
- mismatchDescription.appendText("source code did not use a method reference to invoke " + methodName + ". ");
- context(sourceCode, methodName, mismatchDescription);
- return false;
- }
- }
-
- private void context(String sourceCode, String methodName, Description mismatchDescription) {
- if (!sourceCode.contains(methodName)) {
- mismatchDescription.appendText("You did not appear to invoke the method at all.");
- } else {
- String[] lines = sourceCode.split("\\n");
- mismatchDescription.appendText("Actual invocations: ");
- mismatchDescription.appendValueList("[", ",", "]",
- Arrays.stream(lines).filter(l -> l.contains(methodName)).map(String::trim).collect(toList()));
- }
- }
-
- private Optional getSourceContent(Class> clazz) throws IOException {
- String sourceFileName = getSourceFileName(clazz);
- Optional sourceFile = findPathTo(sourceFileName);
-
- return sourceFile.map(this::toContent);
- }
-
- private Optional findPathTo(String sourceFileName) throws IOException {
- File cwd = new File(".");
- File rootOfProject = findRootOfProject(cwd);
- return findSourceFile(rootOfProject, sourceFileName);
- }
-
- private String toContent(File file) {
- try {
- byte[] encoded = Files.readAllBytes(Paths.get(file.toURI()));
- return StandardCharsets.UTF_8.decode(ByteBuffer.wrap(encoded)).toString();
- } catch (IOException e) {
- throw new RuntimeException("Could not read Java source file.", e);
- }
- }
-
- private Optional findSourceFile(File rootOfProject, String sourceFileName) throws IOException {
- Path startingDir = Paths.get(rootOfProject.toURI());
- return Files.find(startingDir, 15, (path, attrs) -> path.endsWith(sourceFileName))
- .map(p -> new File(p.toUri()))
- .findFirst();
- }
-
- private File findRootOfProject(File cwd) {
- File[] pomFiles = cwd.listFiles((file, name) -> { return name.equals("pom.xml"); });
- if (pomFiles != null && pomFiles.length == 1) {
- return cwd;
- } else if (cwd.getParentFile() == null) {
- throw new RuntimeException("Couldn't find directory containing pom.xml. Last looked in: " + cwd.getAbsolutePath());
- } else {
- return findRootOfProject(cwd.getParentFile());
- }
- }
-
- private String getSourceFileName(Class> clazz) throws IOException {
- String resourceName = clazz.getName().replace(".", "/").concat(".class");
- ClassReader reader = new ClassReader(clazz.getClassLoader().getResourceAsStream(resourceName));
- SourceFileNameVisitor sourceFileNameVisitor = new SourceFileNameVisitor();
- reader.accept(sourceFileNameVisitor, 0);
-
- return sourceFileNameVisitor.getSourceFile();
- }
-
-
- private static final class SourceFileNameVisitor extends ClassVisitor {
-
- private String sourceFile = null;
- private boolean visitedYet = false;
-
- public SourceFileNameVisitor() {
- super(Opcodes.ASM5);
- }
-
- @Override
- public void visitSource(String source, String debug) {
- this.visitedYet = true;
- this.sourceFile = source;
- super.visitSource(source, debug);
- }
-
- public String getSourceFile() {
- if (!visitedYet) throw new IllegalStateException("Must visit a class before asking for source file");
- return this.sourceFile;
- }
- }
-
-}
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java
index 7010d57..005c066 100644
--- a/src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/util/FeatureMatchers.java
@@ -25,7 +25,6 @@
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
-import java.util.function.Function;
/**
* Helper utility for matching on features
@@ -46,14 +45,18 @@ private FeatureMatchers() {}
* @return A Matcher
*/
public static Matcher from(Matcher featureMatcher,
- String description,
- String name,
- Function extractor) {
+ String description,
+ String name,
+ final Extractor extractor) {
return new FeatureMatcher(featureMatcher, description, name) {
@Override protected FEATURE featureValueOf(FROM t) {
- return extractor.apply(t);
+ return extractor.get(t);
}
};
}
+ public static interface Extractor {
+ TO get(FROM from);
+ }
+
}
From b05a4b959a0b4a6690cc55dc38e5e6575b8c2879 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Wed, 8 Apr 2020 22:20:46 +0100
Subject: [PATCH 57/61] Remove outdated instructions and documentation.
In 2020, there's no need to go into detail about getting lambda enabled JDK builds, or how to get support in IDEs.
---
README.md | 32 +++----------------
.../ConfigureYourLambdaBuildOfJdk.java | 10 +++---
2 files changed, 8 insertions(+), 34 deletions(-)
diff --git a/README.md b/README.md
index 00397b6..f0ec9af 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
## Lambda Tutorial [](https://travis-ci.org/AdoptOpenJDK/lambda-tutorial)
-A set of exercises to teach use of Java 8 lambda syntax, and the new Streams API.
+A set of exercises to teach use of Java 8 lambda syntax, and the Streams API.
To follow the exercises:
- fork and clone the repository
- - ensure you have a correctly configured, [lambda-enabled Java build](#getting-lambda-jdk)
- - Maven can help generate configuration for your favourite IDE, though you will likely have to set the JDK manually
+ - ensure you have a correctly configured, JDK8+ build
+ - Maven can help generate configuration for your favourite IDE
- ensure your cloned project, particularly the class `ConfigureYourLambdaBuildOfJdk` compiles and runs correctly
- navigate to the first exercise, `Exercise_1_Test` (tests are in `src/test/java`, in the package `org.adoptopenjdk.lambda.tutorial`)
- read background information in the JavaDoc, and follow instructions, making the test pass
@@ -19,28 +19,4 @@ To follow the exercises:
2. Filtering and Collecting
3. Mapping
4. Method References
-
-[More to come]
-
-
-### Getting Lambda JDK
-Early access builds of JDK 8 are available [here](https://jdk8.java.net/lambda/).
-
-
-#### Lamba JDK Build Compatibility
-The current tutorial is known to work with the following JDK build:
-
-|JDK Build Number|Released On |
-|:---------------|:---------- |
-|ea b109 |Sep 26, 2013|
-
-lambda-tutorial will try to track against the newest version available. If you find that you are working with a newer version of the Lambda JDK and the tutorial does not compile or run, please file an issue.
-
-### IDE Setup
-- [IntelliJ IDEA on Ubuntu](https://github.com/AdoptOpenJDK/lambda-tutorial/wiki/IntelliJ-IDEA-on-Ubuntu-%5BLinux%5D)
-- [IntelliJ IDEA on MacOS](https://github.com/AdoptOpenJDK/lambda-tutorial/wiki/IntelliJ-IDEA-on-MacOS)
-- [IntelliJ IDEA deutsche Anleitung (u.a. Windows)](https://github.com/AdoptOpenJDK/lambda-tutorial/wiki/IntelliJ-IDEA-Einrichtung)
-- [Eclipse Kepler 4.3 on Windows](https://github.com/AdoptOpenJDK/lambda-tutorial/wiki/Eclipse-Lambda-EA-Setup)
-
-Note: we are hoping the instructions are not too sensitive to the OSes on which they have been performed.
-
+ 5. Default methods on interfaces
diff --git a/src/main/java/org/adoptopenjdk/lambda/setupcheck/ConfigureYourLambdaBuildOfJdk.java b/src/main/java/org/adoptopenjdk/lambda/setupcheck/ConfigureYourLambdaBuildOfJdk.java
index 2385664..ea3b5cb 100644
--- a/src/main/java/org/adoptopenjdk/lambda/setupcheck/ConfigureYourLambdaBuildOfJdk.java
+++ b/src/main/java/org/adoptopenjdk/lambda/setupcheck/ConfigureYourLambdaBuildOfJdk.java
@@ -39,15 +39,13 @@ public class ConfigureYourLambdaBuildOfJdk {
public static void main(String... args) {
List messages = asList(
"If this source file does not compile, you have not configured your development setup correctly.",
- "It uses both a new JDK 8 syntax (method references with '::') and a new JDK 8 library method (Iterable#forEach)",
+ "It uses both a JDK 8+ syntax (method references with '::') and a JDK 8+ library method (Iterable#forEach)",
"You should also be able to execute this main method, and see this message printed to the console.",
"",
"To configure your development environment, you need:",
- " - a lambda build of JDK 8, available at: http://jdk8.java.net/lambda/",
- " - a lambda-aware IDE.",
- " IntelliJ and NetBeans support lambdas in early access versions, available at: http://openjdk.java.net/projects/lambda/ \n" +
- " Eclipse support is more sketchy, the method described here just about works: http://tuhrig.de/?p=921",
- " Maven will compile your code and run your tests, just, add JDK 8 javac and java executables to your system path and use 'mvn test'",
+ " - an install of JDK 8 or higher",
+ " - an IDE that supports JDK8+. All mainstream IDEs (Eclipse/IntelliJ IDEA/NetBeans) support JDK8+ ",
+ " - and/or Maven, to compile your code and run your tests, using 'mvn test'",
"",
"Until this source file compiles, you will be unable to make progress in the tutorial.");
From d24ad067f7a211c44c8adfb4544420501f3c7c20 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Thu, 9 Apr 2020 13:29:40 +0100
Subject: [PATCH 58/61] Remove test cases from prejava8 branch.
---
.../lambda/tutorial/Exercise_5_Test.java | 166 ++++++++++++++++++
1 file changed, 166 insertions(+)
create mode 100644 src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_5_Test.java
diff --git a/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_5_Test.java b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_5_Test.java
new file mode 100644
index 0000000..bedf2c3
--- /dev/null
+++ b/src/test/java/org/adoptopenjdk/lambda/tutorial/Exercise_5_Test.java
@@ -0,0 +1,166 @@
+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
+ * .
+ * #L%
+ */
+
+
+import java.util.function.Consumer;
+
+/**
+ * Exercise 5 - Default Methods
+ *
+ * The introduction of Default Methods is intended to allow for libraries to evolve more easily - with the
+ * JDK Collections library as their first main user. They permit adding concrete methods, with an implementation,
+ * to Java interfaces. Prior to JDK 8, every method on an interface had to be abstract, with implementations provided
+ * by classes. With JDK 8, it's possible to declare the following interface:
+ *
+ *
+ *
+ * // (stripped down version of the real Iterable.java)
+ * public interface Iterable {
+ *
+ * Iterator iterator(); // As in prior versions
+ *
+ * // example of a new JDK 8 default method
+ * default void forEach(Consumer super T> action) {
+ * Objects.requireNonNull(action);
+ * for (T t : this) {
+ * action.accept(t);
+ * }
+ * }
+ * }
+ *
+ *
+ * In this case the method forEach is a default method with a concrete implementation. With this
+ * declaration forEach() can be invoked on any implementation of Iterable. This is very
+ * similar to adding a concrete method to an abstract class -- if no implementation overrides it, the code in the method
+ * on the interface is executed. Crucially, the new method can be added without causing compiler errors in the client
+ * code. Consider if a normal forEach method was added to Iterable: every class that
+ * implemented the Iterable interface would now fail to compile. Using default methods, the interface can
+ * be evolved without breaking client code.
+ *
+ * Allowing evolution of a library is the primary use case of default methods.
+ *
+ *
+ *
Rules of Method Lookup
+ *
+ * Inheritance of method implementations is not new to Java. When a subclass method is invoked, the runtime finds the
+ * nearest implementation available. This could be declared on the subclass itself, or on any of it's superclasses, or
+ * even all the way up in java.lang.Object. This last case is what occurs if your class does not override
+ * toString. However, if anywhere in the inheritance hierarchy from your class to Object, toString is
+ * implemented, that would be executed instead of Object's toString. This behaviour is still in
+ * place in JDK 8, but has been augmented to cope with default methods.
+ *
+ *
+ * The main difference between superclass methods and default methods in JDK 8 is that there is only single
+ * inheritance of classes, while there is multiple inheritance of interfaces. As such, there needs to be
+ * some extra rules around method lookup.
+ *
+ * Method lookup has all the following characteristics:
+ *
+ *
For a default method to be invoked, there must be no declaration anywhere in the class hierarchy.
+ *
+ * This can
+ * be thought of like Miranda rights, as in, "You have
+ * the right to a method implementation. If your class hierarchy cannot afford an implementation, one will be
+ * provided for you". A method implementation anywhere in the class hierarchy will take priority over a default
+ * method. This also applies to default methods which match the signature from java.lang.Object; the default method
+ * can never be invoked. As such, declaring a default method which matches a method from Object is a compiler error.
+ *
+ * The closest default method wins.
+ *
+ * As with superclasses, when searching for a concrete method the nearest
+ * superclass wins. For example, given "class A", "class B extends A", and "class C
+ * extends B", if a method is invoked on C, the Java runtime will first look for the implementation in C,
+ * then B, then A, then finally java.lang.Object, invoking the first method it finds. This is also the case with
+ * super-interfaces. Indeed, given "interface X", "interface Y extends X" and "
+ * interface Z extends Y", and declaring "class C extends B, implements Z", the lookup for a
+ * default method would traverse the hierarchy in the following order: C -> B -> A -> Object -> Z -> Y -> X.
+ *
+ * Ambiguous inheritance of a default method must be resolved in the implementing class, at compile time.
+ *
+ * If a class inherits the same default method from more than one source, a compiler error will be emitted. This happens
+ * when unrelated types declare the same method signature, and a class becomes a subtype of more than one of them.
+ * Consider the following example:
+ *
+ * interface A {
+ * default void speak() { System.out.println("A says hi!"; }
+ * }
+ * interface B {
+ * default void speak() { System.out.println("Regards from B!"; }
+ * }
+ * class C implements A, B { } // compiler error: inherits unrelated default methods from A and B
+ *
+ * This principle applies regardless of how deep the interface inheritance hierarchy is, and also applies to
+ * sub-interfaces as well as classes, i.e. "interface Y extends A, B" results in the same compile error.
+ * This error can be resolved by removing ambiguity in the subtype, by overriding the default method. This can be any
+ * compliant implementation, including directly invoking a specific inherited default method. A new syntax in JDK 8 is
+ * available to allow choosing an inherited default method, like so:
+ *
+ * class C implements A, B {
+ * public void speak() { A.super.speak(); } // prints "A says hi!"
+ * }
+ *
+ * In this case, when instanceOfC.speak(); is executed, the default speak from
+ * interface A is invoked. This syntax is also available within default method bodies, allowing an interface
+ * to choose an implementation from one of its super-interfaces. Note that this syntax is not entirely unfamiliar: it
+ * can be considered just like invoking super.someMethod();, except that with the single inheritance of
+ * classes, the name of the super class is can be nothing other than the single superclass, so it can remain implicit.
+ *
+ * It should be noted that ambiguities are never resolved by the order in which interface implementations are
+ * declared (such as with Scala's traits). They must always be resolved explicitly in the subtype.
+ *
+ * Do default methods mean Java supports multiple inheritance?
+ *
+ * In a way. Java has always supported multiple inheritance of interfaces, previously this did not include any of the
+ * implementation, just the contract. Inheritance can be subdivided again, into inheritance of state, and
+ * inheritance of behaviour. Default methods introduce the latter, multiple inheritance of behaviour.
+ *
What about the diamond problem?
+ * There are two aspects to the diamond problem: a) disambiguating implementations and b) handling state. We have seen
+ * how ambiguities are resolved in JDK 8, requiring that users specify the implementation at compile time. Default
+ * methods do not introduce a problem in that aspect of the diamond problem. The second problem, state, is where trickier
+ * issues of the diamond problem live. It can be too easy to accidentally introduce bugs into an implementing class
+ * because it must adhere to the contract defined in the interface and/or superclass, which can include maintaining
+ * state. Any methods which manipulate that state must also be invoked in the implementing class, and accidentally
+ * "losing" that invocation is an easy way to introduce a subtle bug. Also, there is the problem between the ordering of
+ * conflicting methods, that cannot be resolved by disambiguating. Default methods in Java avoid this issue, due to an
+ * existing virtue of interfaces, that they do not contain state. Because an interface cannot be constructed
+ * with fields, there is no state available to encounter this issue. Java still has single inheritance of state,
+ * through superclasses. If the contract of an interface requires state (like, e.g. Iterator) it will be
+ * provided through the single inheritance chain.
+ *
+ *
+ * Note that there are still ways to "simulate" state in interfaces with default methods, since there is no restriction
+ * on accessing static fields defined in other classes. However, that should be avoided for all the reasons that both
+ * multiple inheritance of state, and global mutable state should be avoided.
+ *
+ *
+ * @see Iterable#forEach(Consumer)
+ */
+@SuppressWarnings("unchecked")
+public class Exercise_5_Test {
+
+ /*
+ Exercise on default methods in interfaces does not apply pre-java8.
+ */
+
+}
From fb483da15d5b3ea4034ecc3a27e8e9744900d950 Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Sat, 11 Apr 2020 11:59:36 +0100
Subject: [PATCH 59/61] Update TravisCI config
---
.travis.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 6c23cde..a94fd57 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,6 @@
+dist: precise
language: java
jdk:
- - oraclejdk7
- openjdk7
script: "mvn clean test"
From 781c90f93043196f7db31073e514b49df133e39f Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Sat, 11 Apr 2020 12:04:42 +0100
Subject: [PATCH 60/61] Try to get running on Travis.
---
.travis.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index a94fd57..d0dd95b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,6 @@
dist: precise
language: java
jdk:
- - openjdk7
+ - oraclejdk7
script: "mvn clean test"
From bd653213d92251088dd20748ce6b67b367faeeed Mon Sep 17 00:00:00 2001
From: Graham Allan
Date: Sat, 11 Apr 2020 12:07:23 +0100
Subject: [PATCH 61/61] Still trying to get travis working.
---
.travis.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index d0dd95b..f331bf5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,5 @@
-dist: precise
language: java
jdk:
- - oraclejdk7
+ - openjdk8
script: "mvn clean test"