diff --git a/pom.xml b/pom.xml index fd23cad..3d02b42 100644 --- a/pom.xml +++ b/pom.xml @@ -39,9 +39,6 @@ org.apache.maven.plugins maven-eclipse-plugin - - org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk1.8.0 - true true 1.8 diff --git a/src/main/java/de/codecentric/java8examples/Invoice.java b/src/main/java/de/codecentric/java8examples/Invoice.java new file mode 100644 index 0000000..b510a07 --- /dev/null +++ b/src/main/java/de/codecentric/java8examples/Invoice.java @@ -0,0 +1,73 @@ +package de.codecentric.java8examples; + +import java.math.BigDecimal; +import java.util.List; +import java.util.stream.Collectors; + +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +public class Invoice { + + private String sender; + private String recipient; + private List items; + + public String getSender() { + return sender; + } + + public String getRecipient() { + return recipient; + } + + public List getItems() { + return items; + } + + public BigDecimal getTotal() { + return getItems().stream() + .map(invoice -> invoice + .getPricePerUnit() + .multiply(BigDecimal.valueOf(invoice.getQuantity()))) + .collect(Collectors.reducing( + BigDecimal.ZERO, + (sum, elem) -> sum.add(elem))); + } + + public Invoice(String sender, String recipient, List items) { + this.sender = sender; + this.recipient = recipient; + this.items = items; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Invoice invoice = (Invoice) o; + + if (items != null ? !items.equals(invoice.items) : invoice.items != null) return false; + if (recipient != null ? !recipient.equals(invoice.recipient) : invoice.recipient != null) return false; + if (sender != null ? !sender.equals(invoice.sender) : invoice.sender != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = sender != null ? sender.hashCode() : 0; + result = 31 * result + (recipient != null ? recipient.hashCode() : 0); + result = 31 * result + (items != null ? items.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Invoice{" + + "sender='" + sender + '\'' + + ", recipient='" + recipient + '\'' + + ", items=" + items + + '}'; + } +} diff --git a/src/main/java/de/codecentric/java8examples/InvoiceItem.java b/src/main/java/de/codecentric/java8examples/InvoiceItem.java new file mode 100644 index 0000000..3fb33bf --- /dev/null +++ b/src/main/java/de/codecentric/java8examples/InvoiceItem.java @@ -0,0 +1,59 @@ +package de.codecentric.java8examples; + +import java.math.BigDecimal; + +public class InvoiceItem { + + private String product; + private Integer quantity; + private BigDecimal pricePerUnit; + + public String getProduct() { + return product; + } + + public Integer getQuantity() { + return quantity; + } + + public BigDecimal getPricePerUnit() { + return pricePerUnit; + } + + public InvoiceItem(String product, Integer quantity, BigDecimal pricePerUnit) { + this.product = product; + this.quantity = quantity; + this.pricePerUnit = pricePerUnit; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + InvoiceItem that = (InvoiceItem) o; + + if (pricePerUnit != null ? !pricePerUnit.equals(that.pricePerUnit) : that.pricePerUnit != null) return false; + if (product != null ? !product.equals(that.product) : that.product != null) return false; + if (quantity != null ? !quantity.equals(that.quantity) : that.quantity != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = product != null ? product.hashCode() : 0; + result = 31 * result + (quantity != null ? quantity.hashCode() : 0); + result = 31 * result + (pricePerUnit != null ? pricePerUnit.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "InvoiceItem{" + + "product='" + product + '\'' + + ", quantity=" + quantity + + ", pricePerUnit=" + pricePerUnit + + '}'; + } +} diff --git a/src/main/java/de/codecentric/java8examples/Person.java b/src/main/java/de/codecentric/java8examples/Person.java index d2b3518..6cc8de8 100644 --- a/src/main/java/de/codecentric/java8examples/Person.java +++ b/src/main/java/de/codecentric/java8examples/Person.java @@ -1,7 +1,7 @@ package de.codecentric.java8examples; -import java.util.Calendar; -import java.util.Date; +import java.time.LocalDate; +import java.time.Period; /** * A simple class that represents a person @@ -12,27 +12,27 @@ public enum Gender { MALE, FEMALE } - private final String name; + private final String firstName; private final String lastName; - private final Date birthDay; + private final LocalDate birthDay; private Gender gender; - public Person(String name, String lastName, Date birthDay, Gender gender) { - this.name = name; + public Person(String firstname, String lastName, LocalDate birthDay, Gender gender) { + this.firstName = firstname; this.lastName = lastName; this.birthDay = birthDay; this.gender = gender; } - public String getName() { - return name; + public String getFirstName() { + return firstName; } public String getLastName() { return lastName; } - public Date getBirthDay() { + public LocalDate getBirthDay() { return birthDay; } @@ -45,17 +45,7 @@ public void setGender(Gender gender) { } public int getAge() { - Calendar now = Calendar.getInstance(); - Calendar birth = Calendar.getInstance(); - birth.setTime(getBirthDay()); - - int years = now.get(Calendar.YEAR) - birth.get(Calendar.YEAR); - if(now.get(Calendar.MONTH) > birth.get(Calendar.MONTH) || - now.get(Calendar.MONTH) == birth.get(Calendar.MONTH) && birth.get(Calendar.DATE) > now.get(Calendar.DATE)) { - years--; - } - - return years; + return Period.between(getBirthDay(), LocalDate.now()).getYears(); } @Override @@ -68,14 +58,14 @@ public boolean equals(Object o) { if (birthDay != null ? !birthDay.equals(person.birthDay) : person.birthDay != null) return false; if (gender != person.gender) return false; if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) return false; - if (name != null ? !name.equals(person.name) : person.name != null) return false; + if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) return false; return true; } @Override public int hashCode() { - int result = name != null ? name.hashCode() : 0; + int result = firstName != null ? firstName.hashCode() : 0; result = 31 * result + (lastName != null ? lastName.hashCode() : 0); result = 31 * result + (birthDay != null ? birthDay.hashCode() : 0); result = 31 * result + (gender != null ? gender.hashCode() : 0); @@ -85,7 +75,7 @@ public int hashCode() { @Override public String toString() { return "Person{" + - "name='" + name + '\'' + + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", birthDay=" + birthDay + ", gender=" + gender + diff --git a/src/main/java/de/codecentric/java8examples/defaultmethods/CombinedGreetingService.java b/src/main/java/de/codecentric/java8examples/defaultmethods/CombinedGreetingService.java new file mode 100644 index 0000000..f8db072 --- /dev/null +++ b/src/main/java/de/codecentric/java8examples/defaultmethods/CombinedGreetingService.java @@ -0,0 +1,20 @@ +package de.codecentric.java8examples.defaultmethods; + +/** + * A greeting service implementation that inherits {@link #greet()} from two unrelated interfaces. It has to provide + * an implementation for {@code greet()}. + */ +public class CombinedGreetingService implements GreetingService, AlternativeGreetingService { + + /** + * An implementation of the {@code greet()} method which is defined in both, {@link GreetingService} and + * {@link AlternativeGreetingService}. This implementation simply delegates to the default {@code greet()} + * implementation of the {@code GreetingService} interface + * + * @return the result of calling {@link GreetingService#greet()}. + */ + @Override + public String greet() { + return GreetingService.super.greet(); + } +} diff --git a/src/main/java/de/codecentric/java8examples/defaultmethods/GreetingService.java b/src/main/java/de/codecentric/java8examples/defaultmethods/GreetingService.java index 16e4c6d..473dbd8 100644 --- a/src/main/java/de/codecentric/java8examples/defaultmethods/GreetingService.java +++ b/src/main/java/de/codecentric/java8examples/defaultmethods/GreetingService.java @@ -5,6 +5,11 @@ */ public interface GreetingService { + /** + * Creates a greeting message. The provided default implementation simply returns "Hello world!" + * + * @return A greeting message. + */ default String greet() { return "Hello World!"; } diff --git a/src/main/java/de/codecentric/java8examples/lambdas/SpringJdbcSupportWithLambdas.java b/src/main/java/de/codecentric/java8examples/lambdas/SpringJdbcSupportWithLambdas.java index cebb772..ad1d284 100644 --- a/src/main/java/de/codecentric/java8examples/lambdas/SpringJdbcSupportWithLambdas.java +++ b/src/main/java/de/codecentric/java8examples/lambdas/SpringJdbcSupportWithLambdas.java @@ -19,7 +19,7 @@ public Person findPersonById(String id) { new RowMapper() { @Override public Person mapRow(ResultSet rs, int i) throws SQLException { - return new Person(rs.getString("FIRST_NAME"), rs.getString("LAST_NAME"), rs.getDate(3), Person.Gender.MALE); + return new Person(rs.getString("FIRST_NAME"), rs.getString("LAST_NAME"), rs.getDate(3).toLocalDate(), Person.Gender.MALE); } }); } @@ -27,7 +27,7 @@ public Person mapRow(ResultSet rs, int i) throws SQLException { public Person findPersonByIdWithLambdas(String id) { return getJdbcTemplate().queryForObject( "SELECT * FROM persons WHERE id = " + id, - (rs, i) -> new Person(rs.getString("FIRST_NAME"), rs.getString("LAST_NAME"), rs.getDate(3), Person.Gender.MALE)); + (rs, i) -> new Person(rs.getString("FIRST_NAME"), rs.getString("LAST_NAME"), rs.getDate(3).toLocalDate(), Person.Gender.MALE)); } // if things get messy, use a method reference @@ -40,7 +40,7 @@ private Person mapPerson(ResultSet rs, int i) throws SQLException { return new Person( rs.getString("FIRST_NAME"), rs.getString("LAST_NAME"), - rs.getDate(3), + rs.getDate(3).toLocalDate(), Person.Gender.MALE); } } diff --git a/src/main/java/de/codecentric/java8examples/streaming/CollectingAndReducing.java b/src/main/java/de/codecentric/java8examples/streaming/CollectingAndReducing.java new file mode 100644 index 0000000..597e1dc --- /dev/null +++ b/src/main/java/de/codecentric/java8examples/streaming/CollectingAndReducing.java @@ -0,0 +1,200 @@ +package de.codecentric.java8examples.streaming; + +import de.codecentric.java8examples.Invoice; +import de.codecentric.java8examples.InvoiceItem; +import de.codecentric.java8examples.Person; + +import java.math.BigDecimal; +import java.util.AbstractMap.SimpleEntry; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Your task: Implement the following methods and make the tests passs. + */ +public class CollectingAndReducing { + + /** + * Compute the average age of the given list of Persons. + */ + public static Double averageAge(List persons) { + return persons.stream() + .mapToInt(Person::getAge) + .average().getAsDouble(); + + } + + /** + * How old is the oldest person in the given list. + */ + public static Integer maxAge(List persons) { + return persons.stream() + .mapToInt(Person::getAge) + .max().getAsInt(); + } + + /** + * Compute Age-Statistics (max, min, average, ...) for the given list of Persons. + */ + public static IntSummaryStatistics ageStatistics(List persons) { + return persons.stream() + .mapToInt(Person::getAge) + .summaryStatistics(); + } + + /** + * Build a comma-separated list of the firstnames of a list of Persons. + *

+ * Example-Result: "Maggie, Marge, Mary" + */ + public static String buildCommaSeparatedListOfFirstNames(List persons) { + return persons.stream() + .map(Person::getFirstName) + .collect(Collectors.joining(", ")); + } + + /** + * Identify the cheapest product (by pricePerUnit) in all invoices. + */ + public static String cheapestProduct(List invoices) { + return invoices.stream() + .flatMap(invoice -> invoice.getItems().stream()) + .min(Comparator.comparing(InvoiceItem::getPricePerUnit)) + .get().getProduct(); + } + + /** + * Identify the invoice with the highest total amount. + */ + public static Invoice mostExpensiveInvoice(List invoices) { + return invoices.stream() + .collect(Collectors.maxBy( + Comparator.comparing(Invoice::getTotal))).get(); + } + + /** + * Just what the method name says. + */ + public static Map> groupInvoicesByRecipient(List invoices) { + return invoices.stream() + .collect(Collectors.groupingBy(Invoice::getRecipient)); + } + + /** + * Compute the total amount, that each receiver spent. + *

+ * Hint: Use the two-argument version of Collectors.groupingBy together with Collectors.mapping. + */ + public static Map expensesByRecipient(List invoices) { + return invoices.stream() + .collect(Collectors.groupingBy( + Invoice::getRecipient, + Collectors.mapping( + Invoice::getTotal, + Collectors.reducing( + BigDecimal.ZERO, + (sum, elem) -> sum.add(elem))))); + } + + /** + * How many items of each product have been purchased? + */ + public static Map countByProduct(List invoices) { + return invoices.stream() + .flatMap(invoice -> invoice.getItems().stream()) + .collect(Collectors.groupingBy( + InvoiceItem::getProduct, + Collectors.summingInt(InvoiceItem::getQuantity))); + } + + /** + * For every product, compute the cheapest dealer. Return as a Map where the key is the product name and the value + * is the dealer (=sender of the invoice). + */ + public static Map cheapestDealersByProduct(List invoices) { + return Collections.emptyMap(); + } + + /** + * From a given list of invoices, compute for every dealer the available products together with its price. + */ + public static Map> computeDealerInventory(List invoices) { + Function, String> classifier = + (SimpleEntry entry) -> (String) entry.getKey(); + Function, ProductWithPrice> mapper = + (SimpleEntry entry) -> (ProductWithPrice) entry.getValue(); + + Map> invoicesBySender = invoices.stream() + .collect(Collectors.groupingBy(Invoice::getSender)); + return invoicesBySender.entrySet().stream() + .>flatMap(entry -> entry.getValue().stream() + .flatMap((Invoice invoice) -> invoice.getItems().stream()) + .map((InvoiceItem item) -> new SimpleEntry( + entry.getKey(), + new ProductWithPrice(item.getProduct(), item.getPricePerUnit())))) + .distinct() + .collect(Collectors.groupingBy( + classifier, + Collectors.mapping( + mapper, + Collectors.toList()))); + } + + /** + * For every buyer, compute a list of his favorite products (that is: a list of products ordered by the total count + * of items bought). + * For example: Homer bought 5 beers at Moes, 2 beers and a burger at Crustys. Then the result should look like this: + * {"Homer" -> ["Beer", "Burger"]} + */ + public static Map> favoriteArticlesByBuyer(List invoices) { + return Collections.emptyMap(); + } + + public static class ProductWithPrice { + private String productName; + private BigDecimal price; + + public ProductWithPrice(String productName, BigDecimal price) { + this.productName = productName; + this.price = price; + } + + public String getProductName() { + return productName; + } + + public BigDecimal getPrice() { + return price; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ProductWithPrice that = (ProductWithPrice) o; + + if (price != null ? !price.equals(that.price) : that.price != null) return false; + if (productName != null ? !productName.equals(that.productName) : that.productName != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = productName != null ? productName.hashCode() : 0; + result = 31 * result + (price != null ? price.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "ProductWithPrice{" + + "productName='" + productName + '\'' + + ", price=" + price + + '}'; + } + } + +} diff --git a/src/main/java/de/codecentric/java8examples/streaming/FilteringAndMapping.java b/src/main/java/de/codecentric/java8examples/streaming/FilteringAndMapping.java new file mode 100644 index 0000000..22be36b --- /dev/null +++ b/src/main/java/de/codecentric/java8examples/streaming/FilteringAndMapping.java @@ -0,0 +1,77 @@ +package de.codecentric.java8examples.streaming; + +import java.time.LocalDate; +import java.time.Period; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import de.codecentric.java8examples.Invoice; +import de.codecentric.java8examples.InvoiceItem; +import de.codecentric.java8examples.Person; + +/** + * Your task: Implement the following methods and make the tests passs. + */ +public class FilteringAndMapping { + + /** + * Extract a list of names (firstname and lastname separated by space) from a given list of Person objects. + */ + public static List extractNames(List persons) { + return persons.stream()// + . map(p -> p.getFirstName() + " " + p.getLastName())// + .collect(Collectors. toList()); + } + + /** + * Extract a sorted (ascending by lastname) list of names (firstname and lastname separated by space) from a given list of Person objects. + */ + public static List extractNamesSortedByLastname(List persons) { + return persons.stream()// + .sorted((Person p1, Person p2) -> (p1.getLastName().compareTo(p2.getLastName())))// + . map(p -> p.getFirstName() + " " + p.getLastName())// + .collect(Collectors. toList()); + } + + /** + * From a given list of Person objects, extract a list of female firstnames + */ + public static List extractFemaleFirstnames(List persons) { + return persons.stream()// + .filter(p -> p.getGender().equals(Person.Gender.FEMALE))// + . map(p -> p.getFirstName())// + .collect(Collectors. toList()); + } + + /** + * Extract all females older than 18 years from a given list of Person objects. + */ + public static List extractAdultWomen(List persons) { + return persons.stream()// + .filter(p -> p.getGender().equals(Person.Gender.FEMALE))// + .filter((Person p) -> Period.between(p.getBirthDay(), LocalDate.now()).getYears() >= 18)// + .collect(Collectors. toList()); + } + + /** + * From a given list of Person objects, extract a set of firstnames of the people whose lastname starts with the given string. + */ + public static Set extractFirstnamesWhereLastnameStartsWith(List persons, String startsWith) { + return persons.stream()// + .filter((Person p) -> p.getLastName().startsWith(startsWith))// + . map(p -> p.getFirstName())// + .collect(Collectors. toSet()); + } + + /** + * From a given list of invoices, extract a set of all product names. + */ + public static Set extractAllProducts(List invoices) { + return invoices.stream()// + . flatMap((Invoice i) -> i.getItems().stream())// + . map((InvoiceItem i) -> i.getProduct())// + .collect(Collectors. toSet()); + } +} diff --git a/src/test/java/de/codecentric/java8examples/TestData.java b/src/test/java/de/codecentric/java8examples/TestData.java new file mode 100644 index 0000000..e694e76 --- /dev/null +++ b/src/test/java/de/codecentric/java8examples/TestData.java @@ -0,0 +1,52 @@ +package de.codecentric.java8examples; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Utility class that generates some test data for us. + */ +public class TestData { + public static List listOfPersons() { + return Arrays.asList( + new Person("Jane", "Jungle", LocalDate.of(1978, 12, 15), Person.Gender.FEMALE), + new Person("Mary", "Smith", LocalDate.of(1980, 10, 19), Person.Gender.FEMALE), + new Person("John", "Dole", LocalDate.of(1973, 5, 31), Person.Gender.MALE), + new Person("Michael", "Abrahams", LocalDate.of(1967, 2, 1), Person.Gender.MALE), + new Person("Chris", "Cross", LocalDate.of(1985, 8, 22), Person.Gender.MALE), + new Person("Pete", "Power", LocalDate.of(1981, 3, 18), Person.Gender.MALE), + new Person("Maggie", "Simpson", LocalDate.of(2012, 10, 18), Person.Gender.FEMALE) + ); + } + + public static List listOfInvoices() { + return Arrays.asList( + new Invoice("Crusty Burger", "Homer", Arrays.asList( + new InvoiceItem("Burger", 5, BigDecimal.valueOf(5)), + new InvoiceItem("Coke", 1, BigDecimal.valueOf(5)))), + new Invoice("Crusty Burger", "Bart", Arrays.asList( + new InvoiceItem("Coke", 1, BigDecimal.valueOf(5)))), + new Invoice("Moe", "Homer", Arrays.asList( + new InvoiceItem("Beer", 13, BigDecimal.valueOf(1.5)), + new InvoiceItem("Burger", 3, BigDecimal.valueOf(4.5)))), + new Invoice("Kwik-E-Mart", "Homer", Arrays.asList( + new InvoiceItem("Beer", 9, BigDecimal.valueOf(0.9)), + new InvoiceItem("Chips", 2, BigDecimal.valueOf(0.5)))), + new Invoice("Moe", "Marge", Arrays.asList( + new InvoiceItem("Beer", 1, BigDecimal.valueOf(1.5)))), + new Invoice("Kwik-E-Mart", "Bart", Arrays.asList( + new InvoiceItem("Coke", 2, BigDecimal.valueOf(2.5)), + new InvoiceItem("Chips", 2, BigDecimal.valueOf(0.5)))), + new Invoice("Kwik-E-Mart", "Marge", Arrays.asList( + new InvoiceItem("Cake", 2, BigDecimal.valueOf(3.4)), + new InvoiceItem("Corn Flakes", 5, BigDecimal.valueOf(2.3)))), + new Invoice("Moe", "Homer", Arrays.asList( + new InvoiceItem("Beer", 5, BigDecimal.valueOf(1.5)))), + new Invoice("Flander's Left-Handed Store", "Marge", Arrays.asList( + new InvoiceItem("Left-Handed Scissors", 1, BigDecimal.valueOf(10.0)))) + ); + } +} diff --git a/src/test/java/de/codecentric/java8examples/TestUtil.java b/src/test/java/de/codecentric/java8examples/TestUtil.java deleted file mode 100644 index 36b71ee..0000000 --- a/src/test/java/de/codecentric/java8examples/TestUtil.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.codecentric.java8examples; - -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -/** - * Utility class that generates some test data for us. - */ -public class TestUtil { - - public static Date dateOf(int month, int day, int year) { - return new Date(LocalDate.of(year, month, day).toEpochDay()); - } - - public static List getPersons() { - List list = new ArrayList<>(); - list.add(new Person("Jane", "Jungle", dateOf(12, 15, 1978), Person.Gender.FEMALE)); - list.add(new Person("Mary", "Smith", dateOf(10, 19, 1980), Person.Gender.FEMALE)); - list.add(new Person("John", "Dole", dateOf(5, 31, 1973), Person.Gender.MALE)); - list.add(new Person("Michael", "Abrahams", dateOf(2, 1, 1967), Person.Gender.MALE)); - list.add(new Person("Chris", "Cross", dateOf(8, 22, 1985), Person.Gender.MALE)); - list.add(new Person("Pete", "Power", dateOf(3, 18, 1981), Person.Gender.MALE)); - return list; - } -} diff --git a/src/test/java/de/codecentric/java8examples/defaultmethods/GreetingServiceTest.java b/src/test/java/de/codecentric/java8examples/defaultmethods/GreetingServiceTest.java index 7e3d72e..81a5ee1 100644 --- a/src/test/java/de/codecentric/java8examples/defaultmethods/GreetingServiceTest.java +++ b/src/test/java/de/codecentric/java8examples/defaultmethods/GreetingServiceTest.java @@ -23,4 +23,10 @@ public void greetFromExtended() throws Exception { public void greetFromDerived() throws Exception { assertEquals("Salut le monde!", new DerivedGreetingService().greet()); } + + @Test + public void testName() throws Exception { + assertEquals("Hello World!", new CombinedGreetingService().greet()); + + } } diff --git a/src/test/java/de/codecentric/java8examples/lambdas/LambdaBasedComparatorTest.java b/src/test/java/de/codecentric/java8examples/lambdas/LambdaBasedComparatorTest.java index 1d1a737..e9e7f58 100644 --- a/src/test/java/de/codecentric/java8examples/lambdas/LambdaBasedComparatorTest.java +++ b/src/test/java/de/codecentric/java8examples/lambdas/LambdaBasedComparatorTest.java @@ -1,6 +1,6 @@ package de.codecentric.java8examples.lambdas; -import static de.codecentric.java8examples.TestUtil.getPersons; +import static de.codecentric.java8examples.TestData.listOfPersons; import static org.hamcrest.number.OrderingComparison.lessThanOrEqualTo; import static org.junit.Assert.assertThat; @@ -21,7 +21,7 @@ public class LambdaBasedComparatorTest { @Before public void setUp() throws Exception { - persons = getPersons(); + persons = listOfPersons(); } @Test diff --git a/src/test/java/de/codecentric/java8examples/lambdas/LambdaExampleTest.java b/src/test/java/de/codecentric/java8examples/lambdas/LambdaExampleTest.java index 676c108..3d7dd1d 100644 --- a/src/test/java/de/codecentric/java8examples/lambdas/LambdaExampleTest.java +++ b/src/test/java/de/codecentric/java8examples/lambdas/LambdaExampleTest.java @@ -1,9 +1,9 @@ package de.codecentric.java8examples.lambdas; -import static de.codecentric.java8examples.TestUtil.dateOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.time.LocalDate; import java.util.function.Predicate; import de.codecentric.java8examples.Person; @@ -21,7 +21,7 @@ public class LambdaExampleTest { @Before public void setUp() throws Exception { // Nerd info: 5/15/1962 was the release date of Amazing Fantasy #15, where Spider Man had his first appearance - peter = new Person("Peter", "Parker", dateOf(8, 15, 1962), Person.Gender.MALE); + peter = new Person("Peter", "Parker", LocalDate.of(1962, 8, 15), Person.Gender.MALE); example = new LambdaExample<>(peter); } @@ -34,10 +34,24 @@ public boolean test(Person person) { return person.getAge() > 30; } })); + } + + @Test + public void peterIsOlderThan30WithBlockLambda() throws Exception { + // new: implement the predicate using a block lambda expression + assertTrue(example.matches((Person p) -> { + return p.getAge() > 30; + })); + } - // new: implement the predicate using lambda expression + @Test + public void peterIsOlderThan30WithOneLineLambda() throws Exception { + // implement the predicate using a one line lambda expression assertTrue(example.matches((Person person) -> person.getAge() > 30)); + } + @Test + public void peterIsOlderThan30WithTypeInference() throws Exception { // even shorter: let the compiler work out the correct type assertTrue(example.matches(p -> p.getAge() > 30)); } @@ -46,6 +60,8 @@ public boolean test(Person person) { public void getAgeFromWrappedElementViaFunctionApplication() throws Exception { // type is inferred from context assertEquals("Parker", example.apply(p -> p.getLastName())); + // different notation using a method reference + assertEquals("Parker", example.apply(Person::getLastName)); } @Test diff --git a/src/test/java/de/codecentric/java8examples/methodreference/MethodReferenceExampleTest.java b/src/test/java/de/codecentric/java8examples/methodreference/MethodReferenceExampleTest.java index 783d30b..ebff4c1 100644 --- a/src/test/java/de/codecentric/java8examples/methodreference/MethodReferenceExampleTest.java +++ b/src/test/java/de/codecentric/java8examples/methodreference/MethodReferenceExampleTest.java @@ -20,7 +20,19 @@ public void setUp() throws Exception { } @Test - public void methodReferencToFooMethod() throws Exception { + public void callbackWithoutMethodReference() throws Exception { + Callable callable = new Callable() { + @Override + public String call() throws Exception { + return myFoo.doSomething(); + } + }; + + assertEquals(myFoo.doSomething(), callable.call()); + } + + @Test + public void callbackWithMethodReference() throws Exception { Callable callable = myFoo::doSomething; assertEquals(myFoo.doSomething(), callable.call()); diff --git a/src/test/java/de/codecentric/java8examples/streaming/CollectingAndReducingTest.java b/src/test/java/de/codecentric/java8examples/streaming/CollectingAndReducingTest.java new file mode 100644 index 0000000..6e93505 --- /dev/null +++ b/src/test/java/de/codecentric/java8examples/streaming/CollectingAndReducingTest.java @@ -0,0 +1,164 @@ +package de.codecentric.java8examples.streaming; + +import de.codecentric.java8examples.Invoice; +import de.codecentric.java8examples.InvoiceItem; +import de.codecentric.java8examples.Person; +import de.codecentric.java8examples.TestData; +import org.junit.Test; + +import java.math.BigDecimal; +import java.util.*; + +import static junit.framework.Assert.assertNotNull; +import static org.hamcrest.Matchers.*; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests for mapping and filtering feature of the streaming api. Once you have completed the stub-methods in + * {@link FilteringAndMapping}, these tests should pass. + */ +public class CollectingAndReducingTest { + private List persons = TestData.listOfPersons(); + private List invoices = TestData.listOfInvoices(); + private List recipients = Arrays.asList("Homer", "Bart", "Marge"); + + @Test + public void testAverageAge() throws Exception { + assertThat( + CollectingAndReducing.averageAge(persons), + closeTo(30.57D, 0.01)); + } + + @Test + public void testMaxAge() throws Exception { + assertThat( + CollectingAndReducing.maxAge(persons), + equalTo(46)); + } + + @Test + public void testAgeStatistics() throws Exception { + IntSummaryStatistics statistic = CollectingAndReducing.ageStatistics(persons); + assertNotNull(statistic); + assertThat(statistic.getAverage(), equalTo(30.571428571428573)); + assertThat(statistic.getCount(), equalTo(7l)); + assertThat(statistic.getMax(), equalTo(46)); + assertThat(statistic.getMin(), equalTo(1)); + assertThat(statistic.getSum(), equalTo(214l)); + } + + @Test + public void testBuildCommaSeparatedListOfFirstNames() throws Exception { + assertThat( + CollectingAndReducing.buildCommaSeparatedListOfFirstNames(persons), + equalTo("Jane, Mary, John, Michael, Chris, Pete, Maggie")); + } + + @Test + public void testCheapestProduct() throws Exception { + assertThat( + CollectingAndReducing.cheapestProduct(invoices), + equalTo("Chips")); + } + + @Test + public void testMostExpensiveInvoice() throws Exception { + assertThat( + CollectingAndReducing.mostExpensiveInvoice(invoices), + equalTo(invoices.get(2))); + + } + + @Test + public void testGroupInvoicesByRecipient() throws Exception { + Map> invoicesByRecipient = + CollectingAndReducing.groupInvoicesByRecipient(invoices); + assertThat(invoicesByRecipient.keySet(), hasSize(recipients.size())); + + for (String recipient: recipients) { + for (Invoice invoice: invoices) { + if (recipient.equals(invoice.getRecipient())) { + assertThat(invoicesByRecipient.get(recipient), + hasItem(invoice)); + } else { + assertThat(invoicesByRecipient.get(recipient), + not(hasItem(invoice))); + } + } + } + } + + @Test + public void testExpensesByRecipient() throws Exception { + Map expencesByRecipient = + CollectingAndReducing.expensesByRecipient(invoices); + assertThat(expencesByRecipient.keySet(), hasSize(recipients.size())); + for (String recipient: recipients) { + BigDecimal expenses = BigDecimal.ZERO; + for (Invoice invoice: invoices) { + if (recipient.equals(invoice.getRecipient())) { + expenses = expenses.add(invoice.getTotal()); + } + } + assertThat(expencesByRecipient.get(recipient), equalTo(expenses)); + } + } + + @Test + public void testCountByProduct() throws Exception { + Map expected = new HashMap<>(); + for (Invoice invoice: invoices) { + for (InvoiceItem item: invoice.getItems()) { + String product = item.getProduct(); + if (expected.get(product) == null) { + expected.put(product, Integer.valueOf(0)); + } + expected.put( + product, + expected.get(product) + item.getQuantity()); + } + } + + Map actual = CollectingAndReducing.countByProduct(invoices); + assertThat(actual.keySet(), hasSize(expected.size())); + for (Map.Entry entry: expected.entrySet()) { + assertThat(actual, hasEntry(entry.getKey(), entry.getValue())); + } + } + + @Test + public void testCheapestDealersByProduct() throws Exception { + + } + + @Test + public void testComputeDealerInventory() throws Exception { + HashMap> expected = new HashMap<>(); + for (Invoice invoice: invoices) { + String sender = invoice.getSender(); + if (expected.get(sender) == null) { + expected.put(sender, new ArrayList()); + } + List itemsOfSender = expected.get(sender); + for (InvoiceItem item: invoice.getItems()) { + CollectingAndReducing.ProductWithPrice newItem = new CollectingAndReducing.ProductWithPrice(item.getProduct(), item.getPricePerUnit()); + if (!itemsOfSender.contains(newItem)) { + itemsOfSender.add(newItem); + } + } + } + + Map> actual = + CollectingAndReducing.computeDealerInventory(invoices); + + assertThat(actual.keySet(), hasSize(expected.size())); + for (String sender: expected.keySet()) { + assertThat("Unexpected item set for dealer " + sender, actual.get(sender), containsInAnyOrder(expected.get(sender).toArray())); + } + } + + @Test + public void testFavoriteArticlesByBuyer() throws Exception { + + } +} diff --git a/src/test/java/de/codecentric/java8examples/streaming/FilteringAndMappingTest.java b/src/test/java/de/codecentric/java8examples/streaming/FilteringAndMappingTest.java new file mode 100644 index 0000000..103723a --- /dev/null +++ b/src/test/java/de/codecentric/java8examples/streaming/FilteringAndMappingTest.java @@ -0,0 +1,60 @@ +package de.codecentric.java8examples.streaming; + +import de.codecentric.java8examples.Invoice; +import de.codecentric.java8examples.Person; +import de.codecentric.java8examples.TestData; +import org.hamcrest.MatcherAssert; +import org.junit.Test; + +import java.util.List; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.Assert.assertThat; + +/** + * Tests for mapping and filtering feature of the streaming api. Once you have completed the stub-methods in {@link FilteringAndMapping}, these tests should + * pass. + */ +public class FilteringAndMappingTest { + + private List persons = TestData.listOfPersons(); + + private List invoices = TestData.listOfInvoices(); + + @Test + public void collectFirstNamesOfFemales() throws Exception { + // Matchers.contains should really be called "containsExactlyTheseElementsAndNoOtherElementsInExactlyThisOrder" + // I hate hamcrest :( + assertThat(FilteringAndMapping.extractFemaleFirstnames(persons), contains("Jane", "Mary", "Maggie")); + } + + @Test + public void extractNames() { + assertThat(FilteringAndMapping.extractNames(persons.subList(0, 6)), + contains("Jane Jungle", "Mary Smith", "John Dole", "Michael Abrahams", "Chris Cross", "Pete Power")); + } + + @Test + public void extractNamesSortedByLastname() { + assertThat(FilteringAndMapping.extractNamesSortedByLastname(persons.subList(0, 6)), + contains("Michael Abrahams", "Chris Cross", "John Dole", "Jane Jungle", "Pete Power", "Mary Smith")); + } + + @Test + public void extractAdultWomen() { + // Yes, I know, this test is time-dependent and will break in a few months/years... + assertThat(FilteringAndMapping.extractAdultWomen(persons), contains(persons.get(0), persons.get(1))); + } + + @Test + public void extractFirstnamesWhereLastnameStartsWith() { + assertThat(FilteringAndMapping.extractFirstnamesWhereLastnameStartsWith(persons, "S"), contains("Maggie", "Mary")); + } + + @Test + public void testExtractAllProducts() throws Exception { + MatcherAssert.assertThat(FilteringAndMapping.extractAllProducts(invoices), + containsInAnyOrder("Beer", "Burger", "Corn Flakes", "Chips", "Coke", "Cake", "Left-Handed Scissors")); + } +} diff --git a/src/test/java/de/codecentric/java8examples/streaming/StreamingApiTest.java b/src/test/java/de/codecentric/java8examples/streaming/StreamingApiTest.java deleted file mode 100644 index 142bdbb..0000000 --- a/src/test/java/de/codecentric/java8examples/streaming/StreamingApiTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.codecentric.java8examples.streaming; - -import static de.codecentric.java8examples.TestUtil.getPersons; -import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; -import static org.junit.Assert.assertThat; - -import java.util.List; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.junit.Before; -import org.junit.Test; - -import de.codecentric.java8examples.Person; - -/** - * Test case showing the use of the streaming API. - */ -public class StreamingApiTest { - - private List persons; - - @Before - public void setUp() throws Exception { - persons = getPersons(); - } - - @Test - public void collectLastNamesOfFemales() throws Exception { - List femaleLastNames = - persons.stream() - .filter(p -> p.getGender().equals(Person.Gender.FEMALE)) - . map(p -> p.getLastName()) - .collect(Collectors. toList()); - - assertThat(femaleLastNames, containsInAnyOrder("Jungle", "Smith")); - } - - @Test - public void testName() throws Exception { - Stream stream = Stream.generate(new Supplier() { - int count = 0; - - @Override - public Integer get() { - System.out.println("get" + count); - return count++; - } - }); - - stream - .filter(i -> i.intValue() % 2 == 0) - .limit(100); - - - } -} diff --git a/src/test/java/de/codecentric/java8examples/timeapi/TimeApiTest.java b/src/test/java/de/codecentric/java8examples/timeapi/TimeApiTest.java index 2a6e6e7..419aee6 100644 --- a/src/test/java/de/codecentric/java8examples/timeapi/TimeApiTest.java +++ b/src/test/java/de/codecentric/java8examples/timeapi/TimeApiTest.java @@ -4,28 +4,38 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.chrono.ThaiBuddhistDate; +import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; import org.joda.time.DateTime; import org.joda.time.DateTimeFieldType; import org.joda.time.DateTimeZone; import org.joda.time.Interval; import org.joda.time.chrono.BuddhistChronology; +import org.joda.time.format.DateTimeFormat; import org.junit.Test; +import sun.util.BuddhistCalendar; + /** * Why JSR-310 isn't Joda-Time
* nice presentation (JUG Ukraine)
* project page
- * examples 1
- * examples 2
- * Java-Doc/a>
+ *
Java-Doc
* *
*

Joda

@@ -80,14 +90,22 @@ public void timeBetweenToDates() { Period between = Period.between(birthday, LocalDate.now()); System.out.println(between.getYears() + " Years " + between.getMonths() + " Months " + between.getDays() + " Days."); assertEquals(32, between.getYears()); - assertEquals(1, between.getMonths()); + assertEquals(2, between.getMonths()); // Joda org.joda.time.LocalDate birthdayJoda = new org.joda.time.LocalDate(1981, 9, 3); org.joda.time.Period betweenJoda = new org.joda.time.Period(birthdayJoda, org.joda.time.LocalDate.now()); System.out.println(betweenJoda.getYears() + " Years " + betweenJoda.getMonths() + " Months " + betweenJoda.getDays() + " Days."); assertEquals(32, betweenJoda.getYears()); - assertEquals(1, betweenJoda.getMonths()); + assertEquals(2, betweenJoda.getMonths()); + + // Date & Calendar + Calendar birthdayCalendar = Calendar.getInstance(); + birthdayCalendar.set(1981, Calendar.SEPTEMBER, 3); + int diffYears = Calendar.getInstance().get(Calendar.YEAR) - birthdayCalendar.get(Calendar.YEAR); + int diffMonth = Calendar.getInstance().get(Calendar.MONTH) - birthdayCalendar.get(Calendar.MONTH); + assertEquals(32, diffYears); + assertEquals(2, diffMonth); } @Test @@ -102,11 +120,23 @@ public void checkDateIsInInterval() { Interval interval = new Interval(new DateTime(1981, 9, 3, 12, 0), DateTime.now()); assertFalse(interval.contains(new DateTime(1980, 1, 1, 12, 0))); assertTrue(interval.contains(new DateTime(1985, 1, 1, 12, 0))); + + // Date & Calendar + Calendar birthdayCalendar = Calendar.getInstance(); + birthdayCalendar.set(1981, Calendar.SEPTEMBER, 3); + Calendar nowCalendar = Calendar.getInstance(); + assertFalse(checkInterval(birthdayCalendar, nowCalendar, new GregorianCalendar(1980, 1, 1))); + assertTrue(checkInterval(birthdayCalendar, nowCalendar, new GregorianCalendar(1985, 1, 1))); + } private boolean checkInterval(LocalDate from, LocalDate to, LocalDate date) { return from.isBefore(date) && to.isAfter(date); } + + private boolean checkInterval(Calendar from, Calendar to, Calendar date) { + return date.getTime().after(from.getTime()) && date.getTime().before(to.getTime()); + } @Test public void calculate5Years() { @@ -123,18 +153,24 @@ public void calculate5Years() { assertEquals(birthdayPlusJoda.getDayOfMonth(), 3); assertEquals(birthdayPlusJoda.getMonthOfYear(), 9); assertEquals(birthdayPlusJoda.getYear(), 1991); + + // Date & Calendar + Calendar birthdayCalendar = Calendar.getInstance(); + birthdayCalendar.set(1981, Calendar.SEPTEMBER, 3); + birthdayCalendar.add(Calendar.YEAR,10); + assertEquals(birthdayCalendar.get(Calendar.DAY_OF_MONTH), 3); + // !! 0-based + assertEquals(birthdayCalendar.get(Calendar.MONTH), Calendar.SEPTEMBER); + assertEquals(birthdayCalendar.get(Calendar.YEAR), 1991); } @Test public void chronologies() { // JSR-310 ThaiBuddhistDate thaiBuddhistDate = ThaiBuddhistDate.now(); - System.out.println(thaiBuddhistDate.getChronology()); int thaiYear = thaiBuddhistDate.get(ChronoField.YEAR); LocalDate isoDate = LocalDate.now(); - System.out.println(isoDate.getChronology()); int isoYear = isoDate.get(ChronoField.YEAR); - assertEquals(thaiYear, isoYear + 543); // Joda @@ -142,35 +178,77 @@ public void chronologies() { int buddhistYearJoda = buddhistDateJoda.get(DateTimeFieldType.year()); org.joda.time.LocalDate dateJoda = new org.joda.time.LocalDate(); int yearJoda = dateJoda.get(DateTimeFieldType.year()); - assertEquals(buddhistYearJoda, yearJoda + 543); + + // Date & Calendar + BuddhistCalendar buddhistCalendar = new BuddhistCalendar(); + int buddhistYearCalendar = buddhistCalendar.get(BuddhistCalendar.YEAR); + Calendar calendar = Calendar.getInstance(); + int yearCalendar = calendar.get(Calendar.YEAR); + assertEquals(buddhistYearCalendar, yearCalendar + 543); } + @Test - public void timezoneAndOffset() { + public void timezoneAndOffset() throws InterruptedException { // JSR-310 - ZonedDateTime zonedDateTimeEurope = ZonedDateTime.now(); - ZonedDateTime zonedDateTimeUTC = ZonedDateTime.now(ZoneId.of("UTC")); + LocalDateTime dateTime = LocalDateTime.now(); + ZonedDateTime zonedDateTimeEurope = ZonedDateTime.of(dateTime, ZoneId.systemDefault()); + ZonedDateTime zonedDateTimeUTC = ZonedDateTime.of(dateTime, ZoneId.of("UTC")); - assertEquals(60 * 60 * 2, zonedDateTimeEurope.getOffset().getTotalSeconds()); + assertEquals(60 * 60 * 1, zonedDateTimeEurope.getOffset().getTotalSeconds()); assertEquals("Europe/Berlin", zonedDateTimeEurope.getZone().getId()); - assertFalse(zonedDateTimeEurope.isBefore(zonedDateTimeUTC)); + //!! + assertTrue(zonedDateTimeEurope.isBefore(zonedDateTimeUTC)); assertFalse(zonedDateTimeEurope.isAfter(zonedDateTimeUTC)); - assertTrue(zonedDateTimeEurope.isEqual(zonedDateTimeUTC)); + assertFalse(zonedDateTimeEurope.isEqual(zonedDateTimeUTC)); // Joda - DateTime dateTimeEuropeJoda = DateTime.now(); - DateTime dateTimeUTCJoda = DateTime.now(DateTimeZone.UTC); + Date date = new Date(); + DateTime dateTimeEuropeJoda = new DateTime(date.getTime(),DateTimeZone.getDefault()); + DateTime dateTimeUTCJoda = new DateTime(date.getTime(),DateTimeZone.UTC); - assertEquals(60 * 60 * 2 * 1000, dateTimeEuropeJoda.getZone().getOffset(dateTimeEuropeJoda.getMillis())); + assertEquals(60 * 60 * 1 * 1000, dateTimeEuropeJoda.getZone().getOffset(dateTimeEuropeJoda.getMillis())); assertEquals("Europe/Berlin", dateTimeEuropeJoda.getZone().getID()); + //!! assertFalse(dateTimeEuropeJoda.isBefore(dateTimeUTCJoda)); assertFalse(dateTimeEuropeJoda.isAfter(dateTimeUTCJoda)); assertTrue(dateTimeEuropeJoda.isEqual(dateTimeUTCJoda)); + + // Date & Calendar + Calendar calendarEurope = Calendar.getInstance(); + calendarEurope.setTime(date); + Calendar calendarUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + calendarEurope.setTime(date); + assertFalse(calendarEurope.getTime().before(calendarUTC.getTime())); + assertFalse(calendarEurope.getTime().after(calendarUTC.getTime())); + assertTrue(calendarEurope.getTime().equals(calendarUTC.getTime())); } - // TODO Formatting & Parsing - // TODO Conversion Hibernate? SQL - zurück zu util.date - // TODO Null-Handling und beschriebene Unterschiede + @Test + public void parsingAndFormatting() { + // JSR-310 + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd.MM.yyyy"); + LocalDate parsedLocalDate = LocalDate.parse("03.09.1981", fmt); + assertEquals(LocalDate.of(1981, 9, 3), parsedLocalDate); + assertEquals("03.09.1981", fmt.format(LocalDate.of(1981, 9, 3))); + + // Joda + org.joda.time.format.DateTimeFormatter fmtJoda = DateTimeFormat.forPattern("dd.MM.yyyy"); + org.joda.time.LocalDate parsedDateJoda = org.joda.time.LocalDate.parse("03.09.1981", fmtJoda); + assertEquals(new org.joda.time.LocalDate(1981, 9, 3), parsedDateJoda); + assertEquals("03.09.1981", fmtJoda.print(new org.joda.time.LocalDate(1981, 9, 3))); + + // Date & Calendar + DateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy"); + Date parsedDate = null; + try { + parsedDate= dateFormat.parse("03.09.1981"); + } catch (ParseException e) { + // handle + } + assertEquals(new GregorianCalendar(1981, Calendar.SEPTEMBER,3).getTime(), parsedDate); + assertEquals("03.09.1981", dateFormat.format(new GregorianCalendar(1981, Calendar.SEPTEMBER,3).getTime())); + } } \ No newline at end of file