├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── de │ └── codecentric │ └── java8examples │ ├── Invoice.java │ ├── InvoiceItem.java │ ├── Person.java │ ├── defaultmethods │ ├── AbstractGreetingService.java │ ├── AlternativeGreetingService.java │ ├── CombinedGreetingService.java │ ├── DefaultGreetingService.java │ ├── DerivedGreetingService.java │ ├── ExtendedGreetingService.java │ └── GreetingService.java │ ├── lambdas │ ├── LambdaBasedActionLister.java │ ├── LambdaExample.java │ └── SpringJdbcSupportWithLambdas.java │ ├── methodreference │ └── Foo.java │ └── streaming │ ├── CollectingAndReducing.java │ └── FilteringAndMapping.java └── test └── java └── de └── codecentric └── java8examples ├── TestData.java ├── defaultmethods └── GreetingServiceTest.java ├── lambdas ├── LambdaBasedComparatorTest.java └── LambdaExampleTest.java ├── methodreference └── MethodReferenceExampleTest.java ├── streaming ├── CollectingAndReducingTest.java └── FilteringAndMappingTest.java └── timeapi └── TimeApiTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | target 4 | /.classpath 5 | /.project 6 | /.settings 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains some examples for the new features introuced in Java 8, such as: 2 | 3 | * Default Methods 4 | * Method References 5 | * Lambdas 6 | * Streaming API 7 | * Time API (JSR-310) 8 | 9 | You can get Java 8 from www.jdk8.java.net/download.html 10 | 11 | IDE-Support with Spring Tool Suite 12 | * Download STS 3.4.0 http://spring.io/tools/sts/all 13 | * Install Java 8 Support via Update Site http://dist.springsource.com/snapshot/TOOLS/java8/e43 -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | de.codecentric 8 | java8-examples 9 | 0.1-SNAPSHOT 10 | 11 | 12 | 13 | joda-time 14 | joda-time 15 | 2.3 16 | 17 | 18 | org.springframework 19 | spring-jdbc 20 | 3.2.5.RELEASE 21 | 22 | 23 | junit 24 | junit 25 | 4.11 26 | test 27 | 28 | 29 | org.hamcrest 30 | hamcrest-library 31 | 1.3 32 | test 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-eclipse-plugin 41 | 42 | true 43 | true 44 | 1.8 45 | 1.8 46 | UTF-8 47 | 48 | 2.9 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-compiler-plugin 53 | 54 | 1.8 55 | 1.8 56 | UTF-8 57 | 58 | 3.1 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/Invoice.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | import sun.reflect.generics.reflectiveObjects.NotImplementedException; 8 | 9 | public class Invoice { 10 | 11 | private String sender; 12 | private String recipient; 13 | private List items; 14 | 15 | public String getSender() { 16 | return sender; 17 | } 18 | 19 | public String getRecipient() { 20 | return recipient; 21 | } 22 | 23 | public List getItems() { 24 | return items; 25 | } 26 | 27 | public BigDecimal getTotal() { 28 | return getItems().stream() 29 | .map(invoice -> invoice 30 | .getPricePerUnit() 31 | .multiply(BigDecimal.valueOf(invoice.getQuantity()))) 32 | .collect(Collectors.reducing( 33 | BigDecimal.ZERO, 34 | (sum, elem) -> sum.add(elem))); 35 | } 36 | 37 | public Invoice(String sender, String recipient, List items) { 38 | this.sender = sender; 39 | this.recipient = recipient; 40 | this.items = items; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) return true; 46 | if (o == null || getClass() != o.getClass()) return false; 47 | 48 | Invoice invoice = (Invoice) o; 49 | 50 | if (items != null ? !items.equals(invoice.items) : invoice.items != null) return false; 51 | if (recipient != null ? !recipient.equals(invoice.recipient) : invoice.recipient != null) return false; 52 | if (sender != null ? !sender.equals(invoice.sender) : invoice.sender != null) return false; 53 | 54 | return true; 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | int result = sender != null ? sender.hashCode() : 0; 60 | result = 31 * result + (recipient != null ? recipient.hashCode() : 0); 61 | result = 31 * result + (items != null ? items.hashCode() : 0); 62 | return result; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "Invoice{" + 68 | "sender='" + sender + '\'' + 69 | ", recipient='" + recipient + '\'' + 70 | ", items=" + items + 71 | '}'; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/InvoiceItem.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class InvoiceItem { 6 | 7 | private String product; 8 | private Integer quantity; 9 | private BigDecimal pricePerUnit; 10 | 11 | public String getProduct() { 12 | return product; 13 | } 14 | 15 | public Integer getQuantity() { 16 | return quantity; 17 | } 18 | 19 | public BigDecimal getPricePerUnit() { 20 | return pricePerUnit; 21 | } 22 | 23 | public InvoiceItem(String product, Integer quantity, BigDecimal pricePerUnit) { 24 | this.product = product; 25 | this.quantity = quantity; 26 | this.pricePerUnit = pricePerUnit; 27 | } 28 | 29 | @Override 30 | public boolean equals(Object o) { 31 | if (this == o) return true; 32 | if (o == null || getClass() != o.getClass()) return false; 33 | 34 | InvoiceItem that = (InvoiceItem) o; 35 | 36 | if (pricePerUnit != null ? !pricePerUnit.equals(that.pricePerUnit) : that.pricePerUnit != null) return false; 37 | if (product != null ? !product.equals(that.product) : that.product != null) return false; 38 | if (quantity != null ? !quantity.equals(that.quantity) : that.quantity != null) return false; 39 | 40 | return true; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | int result = product != null ? product.hashCode() : 0; 46 | result = 31 * result + (quantity != null ? quantity.hashCode() : 0); 47 | result = 31 * result + (pricePerUnit != null ? pricePerUnit.hashCode() : 0); 48 | return result; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "InvoiceItem{" + 54 | "product='" + product + '\'' + 55 | ", quantity=" + quantity + 56 | ", pricePerUnit=" + pricePerUnit + 57 | '}'; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/Person.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples; 2 | 3 | import java.time.LocalDate; 4 | import java.time.Period; 5 | 6 | /** 7 | * A simple class that represents a person 8 | */ 9 | public class Person { 10 | 11 | public enum Gender { 12 | MALE, FEMALE 13 | } 14 | 15 | private final String firstName; 16 | private final String lastName; 17 | private final LocalDate birthDay; 18 | private Gender gender; 19 | 20 | public Person(String firstname, String lastName, LocalDate birthDay, Gender gender) { 21 | this.firstName = firstname; 22 | this.lastName = lastName; 23 | this.birthDay = birthDay; 24 | this.gender = gender; 25 | } 26 | 27 | public String getFirstName() { 28 | return firstName; 29 | } 30 | 31 | public String getLastName() { 32 | return lastName; 33 | } 34 | 35 | public LocalDate getBirthDay() { 36 | return birthDay; 37 | } 38 | 39 | public Gender getGender() { 40 | return gender; 41 | } 42 | 43 | public void setGender(Gender gender) { 44 | this.gender = gender; 45 | } 46 | 47 | public int getAge() { 48 | return Period.between(getBirthDay(), LocalDate.now()).getYears(); 49 | } 50 | 51 | @Override 52 | public boolean equals(Object o) { 53 | if (this == o) return true; 54 | if (o == null || getClass() != o.getClass()) return false; 55 | 56 | Person person = (Person) o; 57 | 58 | if (birthDay != null ? !birthDay.equals(person.birthDay) : person.birthDay != null) return false; 59 | if (gender != person.gender) return false; 60 | if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) return false; 61 | if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) return false; 62 | 63 | return true; 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | int result = firstName != null ? firstName.hashCode() : 0; 69 | result = 31 * result + (lastName != null ? lastName.hashCode() : 0); 70 | result = 31 * result + (birthDay != null ? birthDay.hashCode() : 0); 71 | result = 31 * result + (gender != null ? gender.hashCode() : 0); 72 | return result; 73 | } 74 | 75 | @Override 76 | public String toString() { 77 | return "Person{" + 78 | "firstName='" + firstName + '\'' + 79 | ", lastName='" + lastName + '\'' + 80 | ", birthDay=" + birthDay + 81 | ", gender=" + gender + 82 | '}'; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/defaultmethods/AbstractGreetingService.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.defaultmethods; 2 | 3 | /** 4 | * An abstract base class for GreetingService implementations that forces derived classes to implement {@link #greet()} 5 | * by making it abstract. 6 | */ 7 | abstract class AbstractGreetingService implements GreetingService { 8 | 9 | @Override 10 | public abstract String greet(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/defaultmethods/AlternativeGreetingService.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.defaultmethods; 2 | 3 | /** 4 | * An alternative implementation of GreetingService the provides it's own implementation of {@link #greet()}. 5 | */ 6 | public interface AlternativeGreetingService { 7 | 8 | default String greet() { 9 | return "Alternative Greeting!"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/defaultmethods/CombinedGreetingService.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.defaultmethods; 2 | 3 | /** 4 | * A greeting service implementation that inherits {@link #greet()} from two unrelated interfaces. It has to provide 5 | * an implementation for {@code greet()}. 6 | */ 7 | public class CombinedGreetingService implements GreetingService, AlternativeGreetingService { 8 | 9 | /** 10 | * An implementation of the {@code greet()} method which is defined in both, {@link GreetingService} and 11 | * {@link AlternativeGreetingService}. This implementation simply delegates to the default {@code greet()} 12 | * implementation of the {@code GreetingService} interface 13 | * 14 | * @return the result of calling {@link GreetingService#greet()}. 15 | */ 16 | @Override 17 | public String greet() { 18 | return GreetingService.super.greet(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/defaultmethods/DefaultGreetingService.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.defaultmethods; 2 | 3 | /** 4 | * A default implementation of GreetingService that doesn't need to implement anything, because an implementation of 5 | * {@link #greet} is provided by GreetingService as default method. 6 | */ 7 | public class DefaultGreetingService implements GreetingService { 8 | // nothing to implement here... 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/defaultmethods/DerivedGreetingService.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.defaultmethods; 2 | 3 | /** 4 | * A GreetingService implementation that uses {@link AbstractGreetingService} as base class and therefore has to 5 | * redefine {@link #greet()}. 6 | */ 7 | public class DerivedGreetingService extends AbstractGreetingService { 8 | 9 | @Override 10 | public String greet() { 11 | return "Salut le monde!"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/defaultmethods/ExtendedGreetingService.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.defaultmethods; 2 | 3 | /** 4 | * A GreetingService implementation that overrides to provided default method. 5 | */ 6 | public class ExtendedGreetingService implements GreetingService { 7 | 8 | private String name; 9 | 10 | public ExtendedGreetingService(String name) { 11 | this.name = name; 12 | } 13 | 14 | @Override 15 | public String greet() { 16 | return "Hello " + name + "!"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/defaultmethods/GreetingService.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.defaultmethods; 2 | 3 | /** 4 | * A service that greets you. 5 | */ 6 | public interface GreetingService { 7 | 8 | /** 9 | * Creates a greeting message. The provided default implementation simply returns "Hello world!" 10 | * 11 | * @return A greeting message. 12 | */ 13 | default String greet() { 14 | return "Hello World!"; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/lambdas/LambdaBasedActionLister.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.lambdas; 2 | 3 | import javax.swing.*; 4 | 5 | /** 6 | * Test case showing how a {@link java.awt.event.ActionListener} can be implemented using a lambda expression. 7 | */ 8 | public class LambdaBasedActionLister extends JFrame { 9 | 10 | private int clicks = 0; 11 | 12 | public LambdaBasedActionLister() { 13 | JButton btn = new JButton("0"); 14 | btn.setSize(50, 50); 15 | add(btn); 16 | 17 | // lambda magic goes here: 18 | btn.addActionListener(e -> { 19 | btn.setText(Integer.toString(clicks++)); 20 | }); 21 | 22 | setSize(100, 100); 23 | pack(); 24 | setVisible(true); 25 | setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 26 | } 27 | 28 | public static void main(String[] args) { 29 | new LambdaBasedActionLister(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/lambdas/LambdaExample.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.lambdas; 2 | 3 | import java.util.function.Consumer; 4 | import java.util.function.Function; 5 | import java.util.function.Predicate; 6 | 7 | /** 8 | * An example for the use of functional interfaces which can be implemented using lambda expressions. Wraps an 9 | * element of type {@code E} and let's you apply functional interfaces like {@link Function}, {@link Predicate}, and 10 | * {@link Consumer} to the wrapped element. 11 | * 12 | * Look at the LambdaExampleTest to see lambdas in action. 13 | * 14 | * @param The type of the element to be wrapped 15 | */ 16 | public class LambdaExample { 17 | 18 | private E elem; 19 | 20 | public LambdaExample(E elem) { 21 | this.elem = elem; 22 | } 23 | 24 | /** 25 | * Applies the given {@link Function} to the wrapped element and returns the result. 26 | * @param function the function to apply to the wrapped element. 27 | * @param the return type of the function application 28 | * @return the result of applying the given function to the wrapped element. 29 | */ 30 | public R apply(Function function) { 31 | return function.apply(elem); 32 | } 33 | 34 | /** 35 | * Tests whether the wrapped element satisfies the given predicate. 36 | * @param predicate the predicate used for testing 37 | * @return true, if the wrapped element satisfies the given predicate and false otherwise 38 | */ 39 | public boolean matches(Predicate predicate) { 40 | return predicate.test(elem); 41 | } 42 | 43 | /** 44 | * Passes the wrapped element to the given consumer. 45 | * @param consumer the consumer to pass the wrapped element to 46 | */ 47 | public void consume(Consumer consumer) { 48 | consumer.accept(elem); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/lambdas/SpringJdbcSupportWithLambdas.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.lambdas; 2 | 3 | import java.sql.ResultSet; 4 | import java.sql.SQLException; 5 | 6 | import de.codecentric.java8examples.Person; 7 | import org.springframework.jdbc.core.RowMapper; 8 | import org.springframework.jdbc.core.support.JdbcDaoSupport; 9 | 10 | /** 11 | * A class that uses spring JdbcSupport and implements {@link RowMapper RowMappers} using lambda expressions. 12 | */ 13 | public class SpringJdbcSupportWithLambdas extends JdbcDaoSupport { 14 | 15 | public Person findPersonById(String id) { 16 | return getJdbcTemplate().queryForObject( 17 | // not secured against SQL injections, don't do this in production code! 18 | "SELECT * FROM persons WHERE id = " + id, 19 | new RowMapper() { 20 | @Override 21 | public Person mapRow(ResultSet rs, int i) throws SQLException { 22 | return new Person(rs.getString("FIRST_NAME"), rs.getString("LAST_NAME"), rs.getDate(3).toLocalDate(), Person.Gender.MALE); 23 | } 24 | }); 25 | } 26 | 27 | public Person findPersonByIdWithLambdas(String id) { 28 | return getJdbcTemplate().queryForObject( 29 | "SELECT * FROM persons WHERE id = " + id, 30 | (rs, i) -> new Person(rs.getString("FIRST_NAME"), rs.getString("LAST_NAME"), rs.getDate(3).toLocalDate(), Person.Gender.MALE)); 31 | } 32 | 33 | // if things get messy, use a method reference 34 | // overall this is more verbose but maybe better for readability? 35 | public Person findPersonByIfWithMethodReference(String id) { 36 | return getJdbcTemplate().queryForObject("SELECT * FROM persons WHERE id = " + id, this::mapPerson); 37 | } 38 | 39 | private Person mapPerson(ResultSet rs, int i) throws SQLException { 40 | return new Person( 41 | rs.getString("FIRST_NAME"), 42 | rs.getString("LAST_NAME"), 43 | rs.getDate(3).toLocalDate(), 44 | Person.Gender.MALE); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/methodreference/Foo.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.methodreference; 2 | 3 | /** 4 | * A Foo that does something... 5 | */ 6 | public class Foo { 7 | 8 | public String doSomething() { 9 | return "Foo.doSomething()"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/streaming/CollectingAndReducing.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.streaming; 2 | 3 | import de.codecentric.java8examples.Invoice; 4 | import de.codecentric.java8examples.InvoiceItem; 5 | import de.codecentric.java8examples.Person; 6 | 7 | import java.math.BigDecimal; 8 | import java.util.AbstractMap.SimpleEntry; 9 | import java.util.*; 10 | import java.util.function.Function; 11 | import java.util.stream.Collectors; 12 | 13 | /** 14 | * Your task: Implement the following methods and make the tests passs. 15 | */ 16 | public class CollectingAndReducing { 17 | 18 | /** 19 | * Compute the average age of the given list of Persons. 20 | */ 21 | public static Double averageAge(List persons) { 22 | return persons.stream() 23 | .mapToInt(Person::getAge) 24 | .average().getAsDouble(); 25 | 26 | } 27 | 28 | /** 29 | * How old is the oldest person in the given list. 30 | */ 31 | public static Integer maxAge(List persons) { 32 | return persons.stream() 33 | .mapToInt(Person::getAge) 34 | .max().getAsInt(); 35 | } 36 | 37 | /** 38 | * Compute Age-Statistics (max, min, average, ...) for the given list of Persons. 39 | */ 40 | public static IntSummaryStatistics ageStatistics(List persons) { 41 | return persons.stream() 42 | .mapToInt(Person::getAge) 43 | .summaryStatistics(); 44 | } 45 | 46 | /** 47 | * Build a comma-separated list of the firstnames of a list of Persons. 48 | *

49 | * Example-Result: "Maggie, Marge, Mary" 50 | */ 51 | public static String buildCommaSeparatedListOfFirstNames(List persons) { 52 | return persons.stream() 53 | .map(Person::getFirstName) 54 | .collect(Collectors.joining(", ")); 55 | } 56 | 57 | /** 58 | * Identify the cheapest product (by pricePerUnit) in all invoices. 59 | */ 60 | public static String cheapestProduct(List invoices) { 61 | return invoices.stream() 62 | .flatMap(invoice -> invoice.getItems().stream()) 63 | .min(Comparator.comparing(InvoiceItem::getPricePerUnit)) 64 | .get().getProduct(); 65 | } 66 | 67 | /** 68 | * Identify the invoice with the highest total amount. 69 | */ 70 | public static Invoice mostExpensiveInvoice(List invoices) { 71 | return invoices.stream() 72 | .collect(Collectors.maxBy( 73 | Comparator.comparing(Invoice::getTotal))).get(); 74 | } 75 | 76 | /** 77 | * Just what the method name says. 78 | */ 79 | public static Map> groupInvoicesByRecipient(List invoices) { 80 | return invoices.stream() 81 | .collect(Collectors.groupingBy(Invoice::getRecipient)); 82 | } 83 | 84 | /** 85 | * Compute the total amount, that each receiver spent. 86 | *

87 | * Hint: Use the two-argument version of Collectors.groupingBy together with Collectors.mapping. 88 | */ 89 | public static Map expensesByRecipient(List invoices) { 90 | return invoices.stream() 91 | .collect(Collectors.groupingBy( 92 | Invoice::getRecipient, 93 | Collectors.mapping( 94 | Invoice::getTotal, 95 | Collectors.reducing( 96 | BigDecimal.ZERO, 97 | (sum, elem) -> sum.add(elem))))); 98 | } 99 | 100 | /** 101 | * How many items of each product have been purchased? 102 | */ 103 | public static Map countByProduct(List invoices) { 104 | return invoices.stream() 105 | .flatMap(invoice -> invoice.getItems().stream()) 106 | .collect(Collectors.groupingBy( 107 | InvoiceItem::getProduct, 108 | Collectors.summingInt(InvoiceItem::getQuantity))); 109 | } 110 | 111 | /** 112 | * For every product, compute the cheapest dealer. Return as a Map where the key is the product name and the value 113 | * is the dealer (=sender of the invoice). 114 | */ 115 | public static Map cheapestDealersByProduct(List invoices) { 116 | return Collections.emptyMap(); 117 | } 118 | 119 | /** 120 | * From a given list of invoices, compute for every dealer the available products together with its price. 121 | */ 122 | public static Map> computeDealerInventory(List invoices) { 123 | Function, String> classifier = 124 | (SimpleEntry entry) -> (String) entry.getKey(); 125 | Function, ProductWithPrice> mapper = 126 | (SimpleEntry entry) -> (ProductWithPrice) entry.getValue(); 127 | 128 | Map> invoicesBySender = invoices.stream() 129 | .collect(Collectors.groupingBy(Invoice::getSender)); 130 | return invoicesBySender.entrySet().stream() 131 | .>flatMap(entry -> entry.getValue().stream() 132 | .flatMap((Invoice invoice) -> invoice.getItems().stream()) 133 | .map((InvoiceItem item) -> new SimpleEntry( 134 | entry.getKey(), 135 | new ProductWithPrice(item.getProduct(), item.getPricePerUnit())))) 136 | .distinct() 137 | .collect(Collectors.groupingBy( 138 | classifier, 139 | Collectors.mapping( 140 | mapper, 141 | Collectors.toList()))); 142 | } 143 | 144 | /** 145 | * For every buyer, compute a list of his favorite products (that is: a list of products ordered by the total count 146 | * of items bought). 147 | * For example: Homer bought 5 beers at Moes, 2 beers and a burger at Crustys. Then the result should look like this: 148 | * {"Homer" -> ["Beer", "Burger"]} 149 | */ 150 | public static Map> favoriteArticlesByBuyer(List invoices) { 151 | return Collections.emptyMap(); 152 | } 153 | 154 | public static class ProductWithPrice { 155 | private String productName; 156 | private BigDecimal price; 157 | 158 | public ProductWithPrice(String productName, BigDecimal price) { 159 | this.productName = productName; 160 | this.price = price; 161 | } 162 | 163 | public String getProductName() { 164 | return productName; 165 | } 166 | 167 | public BigDecimal getPrice() { 168 | return price; 169 | } 170 | 171 | @Override 172 | public boolean equals(Object o) { 173 | if (this == o) return true; 174 | if (o == null || getClass() != o.getClass()) return false; 175 | 176 | ProductWithPrice that = (ProductWithPrice) o; 177 | 178 | if (price != null ? !price.equals(that.price) : that.price != null) return false; 179 | if (productName != null ? !productName.equals(that.productName) : that.productName != null) return false; 180 | 181 | return true; 182 | } 183 | 184 | @Override 185 | public int hashCode() { 186 | int result = productName != null ? productName.hashCode() : 0; 187 | result = 31 * result + (price != null ? price.hashCode() : 0); 188 | return result; 189 | } 190 | 191 | @Override 192 | public String toString() { 193 | return "ProductWithPrice{" + 194 | "productName='" + productName + '\'' + 195 | ", price=" + price + 196 | '}'; 197 | } 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/java8examples/streaming/FilteringAndMapping.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.streaming; 2 | 3 | import java.time.LocalDate; 4 | import java.time.Period; 5 | import java.util.List; 6 | import java.util.Set; 7 | import java.util.stream.Collectors; 8 | import java.util.stream.Stream; 9 | 10 | import de.codecentric.java8examples.Invoice; 11 | import de.codecentric.java8examples.InvoiceItem; 12 | import de.codecentric.java8examples.Person; 13 | 14 | /** 15 | * Your task: Implement the following methods and make the tests passs. 16 | */ 17 | public class FilteringAndMapping { 18 | 19 | /** 20 | * Extract a list of names (firstname and lastname separated by space) from a given list of Person objects. 21 | */ 22 | public static List extractNames(List persons) { 23 | return persons.stream()// 24 | . map(p -> p.getFirstName() + " " + p.getLastName())// 25 | .collect(Collectors. toList()); 26 | } 27 | 28 | /** 29 | * Extract a sorted (ascending by lastname) list of names (firstname and lastname separated by space) from a given list of Person objects. 30 | */ 31 | public static List extractNamesSortedByLastname(List persons) { 32 | return persons.stream()// 33 | .sorted((Person p1, Person p2) -> (p1.getLastName().compareTo(p2.getLastName())))// 34 | . map(p -> p.getFirstName() + " " + p.getLastName())// 35 | .collect(Collectors. toList()); 36 | } 37 | 38 | /** 39 | * From a given list of Person objects, extract a list of female firstnames 40 | */ 41 | public static List extractFemaleFirstnames(List persons) { 42 | return persons.stream()// 43 | .filter(p -> p.getGender().equals(Person.Gender.FEMALE))// 44 | . map(p -> p.getFirstName())// 45 | .collect(Collectors. toList()); 46 | } 47 | 48 | /** 49 | * Extract all females older than 18 years from a given list of Person objects. 50 | */ 51 | public static List extractAdultWomen(List persons) { 52 | return persons.stream()// 53 | .filter(p -> p.getGender().equals(Person.Gender.FEMALE))// 54 | .filter((Person p) -> Period.between(p.getBirthDay(), LocalDate.now()).getYears() >= 18)// 55 | .collect(Collectors. toList()); 56 | } 57 | 58 | /** 59 | * From a given list of Person objects, extract a set of firstnames of the people whose lastname starts with the given string. 60 | */ 61 | public static Set extractFirstnamesWhereLastnameStartsWith(List persons, String startsWith) { 62 | return persons.stream()// 63 | .filter((Person p) -> p.getLastName().startsWith(startsWith))// 64 | . map(p -> p.getFirstName())// 65 | .collect(Collectors. toSet()); 66 | } 67 | 68 | /** 69 | * From a given list of invoices, extract a set of all product names. 70 | */ 71 | public static Set extractAllProducts(List invoices) { 72 | return invoices.stream()// 73 | . flatMap((Invoice i) -> i.getItems().stream())// 74 | . map((InvoiceItem i) -> i.getProduct())// 75 | .collect(Collectors. toSet()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/de/codecentric/java8examples/TestData.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples; 2 | 3 | import java.math.BigDecimal; 4 | import java.time.LocalDate; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | /** 10 | * Utility class that generates some test data for us. 11 | */ 12 | public class TestData { 13 | public static List listOfPersons() { 14 | return Arrays.asList( 15 | new Person("Jane", "Jungle", LocalDate.of(1978, 12, 15), Person.Gender.FEMALE), 16 | new Person("Mary", "Smith", LocalDate.of(1980, 10, 19), Person.Gender.FEMALE), 17 | new Person("John", "Dole", LocalDate.of(1973, 5, 31), Person.Gender.MALE), 18 | new Person("Michael", "Abrahams", LocalDate.of(1967, 2, 1), Person.Gender.MALE), 19 | new Person("Chris", "Cross", LocalDate.of(1985, 8, 22), Person.Gender.MALE), 20 | new Person("Pete", "Power", LocalDate.of(1981, 3, 18), Person.Gender.MALE), 21 | new Person("Maggie", "Simpson", LocalDate.of(2012, 10, 18), Person.Gender.FEMALE) 22 | ); 23 | } 24 | 25 | public static List listOfInvoices() { 26 | return Arrays.asList( 27 | new Invoice("Crusty Burger", "Homer", Arrays.asList( 28 | new InvoiceItem("Burger", 5, BigDecimal.valueOf(5)), 29 | new InvoiceItem("Coke", 1, BigDecimal.valueOf(5)))), 30 | new Invoice("Crusty Burger", "Bart", Arrays.asList( 31 | new InvoiceItem("Coke", 1, BigDecimal.valueOf(5)))), 32 | new Invoice("Moe", "Homer", Arrays.asList( 33 | new InvoiceItem("Beer", 13, BigDecimal.valueOf(1.5)), 34 | new InvoiceItem("Burger", 3, BigDecimal.valueOf(4.5)))), 35 | new Invoice("Kwik-E-Mart", "Homer", Arrays.asList( 36 | new InvoiceItem("Beer", 9, BigDecimal.valueOf(0.9)), 37 | new InvoiceItem("Chips", 2, BigDecimal.valueOf(0.5)))), 38 | new Invoice("Moe", "Marge", Arrays.asList( 39 | new InvoiceItem("Beer", 1, BigDecimal.valueOf(1.5)))), 40 | new Invoice("Kwik-E-Mart", "Bart", Arrays.asList( 41 | new InvoiceItem("Coke", 2, BigDecimal.valueOf(2.5)), 42 | new InvoiceItem("Chips", 2, BigDecimal.valueOf(0.5)))), 43 | new Invoice("Kwik-E-Mart", "Marge", Arrays.asList( 44 | new InvoiceItem("Cake", 2, BigDecimal.valueOf(3.4)), 45 | new InvoiceItem("Corn Flakes", 5, BigDecimal.valueOf(2.3)))), 46 | new Invoice("Moe", "Homer", Arrays.asList( 47 | new InvoiceItem("Beer", 5, BigDecimal.valueOf(1.5)))), 48 | new Invoice("Flander's Left-Handed Store", "Marge", Arrays.asList( 49 | new InvoiceItem("Left-Handed Scissors", 1, BigDecimal.valueOf(10.0)))) 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/de/codecentric/java8examples/defaultmethods/GreetingServiceTest.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.defaultmethods; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Test case showing how default methods work. 9 | */ 10 | public class GreetingServiceTest { 11 | 12 | @Test 13 | public void greetFromDefault() throws Exception { 14 | assertEquals("Hello World!", new DefaultGreetingService().greet()); 15 | } 16 | 17 | @Test 18 | public void greetFromExtended() throws Exception { 19 | assertEquals("Hello Pete!", new ExtendedGreetingService("Pete").greet()); 20 | } 21 | 22 | @Test 23 | public void greetFromDerived() throws Exception { 24 | assertEquals("Salut le monde!", new DerivedGreetingService().greet()); 25 | } 26 | 27 | @Test 28 | public void testName() throws Exception { 29 | assertEquals("Hello World!", new CombinedGreetingService().greet()); 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/de/codecentric/java8examples/lambdas/LambdaBasedComparatorTest.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.lambdas; 2 | 3 | import static de.codecentric.java8examples.TestData.listOfPersons; 4 | import static org.hamcrest.number.OrderingComparison.lessThanOrEqualTo; 5 | import static org.junit.Assert.assertThat; 6 | 7 | import java.util.Collections; 8 | import java.util.Comparator; 9 | import java.util.List; 10 | 11 | import de.codecentric.java8examples.Person; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | 15 | /** 16 | * Test case showing how a {@link Comparator} can be implemented using a lambda expression. 17 | */ 18 | public class LambdaBasedComparatorTest { 19 | 20 | private List persons; 21 | 22 | @Before 23 | public void setUp() throws Exception { 24 | persons = listOfPersons(); 25 | } 26 | 27 | @Test 28 | public void lambdaBasedComparator() throws Exception { 29 | // comparator is defined on the fly 30 | Comparator byLastNameAsc = (p1, p2) -> p1.getLastName().compareTo(p2.getLastName()); 31 | 32 | Collections.sort(persons, byLastNameAsc); 33 | 34 | for (int i = 0; i < persons.size(); i++) { 35 | Person current = persons.get(i); 36 | if (i < persons.size() - 1) { 37 | Person next = persons.get(i + 1); 38 | assertThat(current.getLastName().compareTo(next.getLastName()), lessThanOrEqualTo(0)); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/de/codecentric/java8examples/lambdas/LambdaExampleTest.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.lambdas; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.time.LocalDate; 7 | import java.util.function.Predicate; 8 | 9 | import de.codecentric.java8examples.Person; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | /** 14 | * Test case showing how lambda expressions work. 15 | */ 16 | public class LambdaExampleTest { 17 | 18 | private LambdaExample example; 19 | private Person peter; 20 | 21 | @Before 22 | public void setUp() throws Exception { 23 | // Nerd info: 5/15/1962 was the release date of Amazing Fantasy #15, where Spider Man had his first appearance 24 | peter = new Person("Peter", "Parker", LocalDate.of(1962, 8, 15), Person.Gender.MALE); 25 | example = new LambdaExample<>(peter); 26 | } 27 | 28 | @Test 29 | public void peterIsOlderThan30() throws Exception { 30 | // old school 31 | assertTrue(example.matches(new Predicate() { 32 | @Override 33 | public boolean test(Person person) { 34 | return person.getAge() > 30; 35 | } 36 | })); 37 | } 38 | 39 | @Test 40 | public void peterIsOlderThan30WithBlockLambda() throws Exception { 41 | // new: implement the predicate using a block lambda expression 42 | assertTrue(example.matches((Person p) -> { 43 | return p.getAge() > 30; 44 | })); 45 | } 46 | 47 | @Test 48 | public void peterIsOlderThan30WithOneLineLambda() throws Exception { 49 | // implement the predicate using a one line lambda expression 50 | assertTrue(example.matches((Person person) -> person.getAge() > 30)); 51 | } 52 | 53 | @Test 54 | public void peterIsOlderThan30WithTypeInference() throws Exception { 55 | // even shorter: let the compiler work out the correct type 56 | assertTrue(example.matches(p -> p.getAge() > 30)); 57 | } 58 | 59 | @Test 60 | public void getAgeFromWrappedElementViaFunctionApplication() throws Exception { 61 | // type is inferred from context 62 | assertEquals("Parker", example.apply(p -> p.getLastName())); 63 | // different notation using a method reference 64 | assertEquals("Parker", example.apply(Person::getLastName)); 65 | } 66 | 67 | @Test 68 | public void changeStateOfWrappedElementViaConsumer() throws Exception { 69 | // this will change the state of the wrapped element! 70 | example.consume(p -> p.setGender(oppositeOf(p.getGender()))); 71 | 72 | assertEquals(Person.Gender.FEMALE, peter.getGender()); 73 | } 74 | 75 | private static Person.Gender oppositeOf(Person.Gender gender) { 76 | if (gender.equals(Person.Gender.MALE)) 77 | return Person.Gender.FEMALE; 78 | else 79 | return Person.Gender.MALE; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/de/codecentric/java8examples/methodreference/MethodReferenceExampleTest.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.methodreference; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.concurrent.Callable; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | /** 11 | * Test case showing the use of method references. 12 | */ 13 | public class MethodReferenceExampleTest { 14 | 15 | private Foo myFoo; 16 | 17 | @Before 18 | public void setUp() throws Exception { 19 | myFoo = new Foo(); 20 | } 21 | 22 | @Test 23 | public void callbackWithoutMethodReference() throws Exception { 24 | Callable callable = new Callable() { 25 | @Override 26 | public String call() throws Exception { 27 | return myFoo.doSomething(); 28 | } 29 | }; 30 | 31 | assertEquals(myFoo.doSomething(), callable.call()); 32 | } 33 | 34 | @Test 35 | public void callbackWithMethodReference() throws Exception { 36 | Callable callable = myFoo::doSomething; 37 | 38 | assertEquals(myFoo.doSomething(), callable.call()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/de/codecentric/java8examples/streaming/CollectingAndReducingTest.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.streaming; 2 | 3 | import de.codecentric.java8examples.Invoice; 4 | import de.codecentric.java8examples.InvoiceItem; 5 | import de.codecentric.java8examples.Person; 6 | import de.codecentric.java8examples.TestData; 7 | import org.junit.Test; 8 | 9 | import java.math.BigDecimal; 10 | import java.util.*; 11 | 12 | import static junit.framework.Assert.assertNotNull; 13 | import static org.hamcrest.Matchers.*; 14 | import static org.hamcrest.MatcherAssert.assertThat; 15 | 16 | /** 17 | * Tests for mapping and filtering feature of the streaming api. Once you have completed the stub-methods in 18 | * {@link FilteringAndMapping}, these tests should pass. 19 | */ 20 | public class CollectingAndReducingTest { 21 | private List persons = TestData.listOfPersons(); 22 | private List invoices = TestData.listOfInvoices(); 23 | private List recipients = Arrays.asList("Homer", "Bart", "Marge"); 24 | 25 | @Test 26 | public void testAverageAge() throws Exception { 27 | assertThat( 28 | CollectingAndReducing.averageAge(persons), 29 | closeTo(30.57D, 0.01)); 30 | } 31 | 32 | @Test 33 | public void testMaxAge() throws Exception { 34 | assertThat( 35 | CollectingAndReducing.maxAge(persons), 36 | equalTo(46)); 37 | } 38 | 39 | @Test 40 | public void testAgeStatistics() throws Exception { 41 | IntSummaryStatistics statistic = CollectingAndReducing.ageStatistics(persons); 42 | assertNotNull(statistic); 43 | assertThat(statistic.getAverage(), equalTo(30.571428571428573)); 44 | assertThat(statistic.getCount(), equalTo(7l)); 45 | assertThat(statistic.getMax(), equalTo(46)); 46 | assertThat(statistic.getMin(), equalTo(1)); 47 | assertThat(statistic.getSum(), equalTo(214l)); 48 | } 49 | 50 | @Test 51 | public void testBuildCommaSeparatedListOfFirstNames() throws Exception { 52 | assertThat( 53 | CollectingAndReducing.buildCommaSeparatedListOfFirstNames(persons), 54 | equalTo("Jane, Mary, John, Michael, Chris, Pete, Maggie")); 55 | } 56 | 57 | @Test 58 | public void testCheapestProduct() throws Exception { 59 | assertThat( 60 | CollectingAndReducing.cheapestProduct(invoices), 61 | equalTo("Chips")); 62 | } 63 | 64 | @Test 65 | public void testMostExpensiveInvoice() throws Exception { 66 | assertThat( 67 | CollectingAndReducing.mostExpensiveInvoice(invoices), 68 | equalTo(invoices.get(2))); 69 | 70 | } 71 | 72 | @Test 73 | public void testGroupInvoicesByRecipient() throws Exception { 74 | Map> invoicesByRecipient = 75 | CollectingAndReducing.groupInvoicesByRecipient(invoices); 76 | assertThat(invoicesByRecipient.keySet(), hasSize(recipients.size())); 77 | 78 | for (String recipient: recipients) { 79 | for (Invoice invoice: invoices) { 80 | if (recipient.equals(invoice.getRecipient())) { 81 | assertThat(invoicesByRecipient.get(recipient), 82 | hasItem(invoice)); 83 | } else { 84 | assertThat(invoicesByRecipient.get(recipient), 85 | not(hasItem(invoice))); 86 | } 87 | } 88 | } 89 | } 90 | 91 | @Test 92 | public void testExpensesByRecipient() throws Exception { 93 | Map expencesByRecipient = 94 | CollectingAndReducing.expensesByRecipient(invoices); 95 | assertThat(expencesByRecipient.keySet(), hasSize(recipients.size())); 96 | for (String recipient: recipients) { 97 | BigDecimal expenses = BigDecimal.ZERO; 98 | for (Invoice invoice: invoices) { 99 | if (recipient.equals(invoice.getRecipient())) { 100 | expenses = expenses.add(invoice.getTotal()); 101 | } 102 | } 103 | assertThat(expencesByRecipient.get(recipient), equalTo(expenses)); 104 | } 105 | } 106 | 107 | @Test 108 | public void testCountByProduct() throws Exception { 109 | Map expected = new HashMap<>(); 110 | for (Invoice invoice: invoices) { 111 | for (InvoiceItem item: invoice.getItems()) { 112 | String product = item.getProduct(); 113 | if (expected.get(product) == null) { 114 | expected.put(product, Integer.valueOf(0)); 115 | } 116 | expected.put( 117 | product, 118 | expected.get(product) + item.getQuantity()); 119 | } 120 | } 121 | 122 | Map actual = CollectingAndReducing.countByProduct(invoices); 123 | assertThat(actual.keySet(), hasSize(expected.size())); 124 | for (Map.Entry entry: expected.entrySet()) { 125 | assertThat(actual, hasEntry(entry.getKey(), entry.getValue())); 126 | } 127 | } 128 | 129 | @Test 130 | public void testCheapestDealersByProduct() throws Exception { 131 | 132 | } 133 | 134 | @Test 135 | public void testComputeDealerInventory() throws Exception { 136 | HashMap> expected = new HashMap<>(); 137 | for (Invoice invoice: invoices) { 138 | String sender = invoice.getSender(); 139 | if (expected.get(sender) == null) { 140 | expected.put(sender, new ArrayList()); 141 | } 142 | List itemsOfSender = expected.get(sender); 143 | for (InvoiceItem item: invoice.getItems()) { 144 | CollectingAndReducing.ProductWithPrice newItem = new CollectingAndReducing.ProductWithPrice(item.getProduct(), item.getPricePerUnit()); 145 | if (!itemsOfSender.contains(newItem)) { 146 | itemsOfSender.add(newItem); 147 | } 148 | } 149 | } 150 | 151 | Map> actual = 152 | CollectingAndReducing.computeDealerInventory(invoices); 153 | 154 | assertThat(actual.keySet(), hasSize(expected.size())); 155 | for (String sender: expected.keySet()) { 156 | assertThat("Unexpected item set for dealer " + sender, actual.get(sender), containsInAnyOrder(expected.get(sender).toArray())); 157 | } 158 | } 159 | 160 | @Test 161 | public void testFavoriteArticlesByBuyer() throws Exception { 162 | 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/test/java/de/codecentric/java8examples/streaming/FilteringAndMappingTest.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.streaming; 2 | 3 | import de.codecentric.java8examples.Invoice; 4 | import de.codecentric.java8examples.Person; 5 | import de.codecentric.java8examples.TestData; 6 | import org.hamcrest.MatcherAssert; 7 | import org.junit.Test; 8 | 9 | import java.util.List; 10 | 11 | import static org.hamcrest.Matchers.contains; 12 | import static org.hamcrest.Matchers.containsInAnyOrder; 13 | import static org.junit.Assert.assertThat; 14 | 15 | /** 16 | * Tests for mapping and filtering feature of the streaming api. Once you have completed the stub-methods in {@link FilteringAndMapping}, these tests should 17 | * pass. 18 | */ 19 | public class FilteringAndMappingTest { 20 | 21 | private List persons = TestData.listOfPersons(); 22 | 23 | private List invoices = TestData.listOfInvoices(); 24 | 25 | @Test 26 | public void collectFirstNamesOfFemales() throws Exception { 27 | // Matchers.contains should really be called "containsExactlyTheseElementsAndNoOtherElementsInExactlyThisOrder" 28 | // I hate hamcrest :( 29 | assertThat(FilteringAndMapping.extractFemaleFirstnames(persons), contains("Jane", "Mary", "Maggie")); 30 | } 31 | 32 | @Test 33 | public void extractNames() { 34 | assertThat(FilteringAndMapping.extractNames(persons.subList(0, 6)), 35 | contains("Jane Jungle", "Mary Smith", "John Dole", "Michael Abrahams", "Chris Cross", "Pete Power")); 36 | } 37 | 38 | @Test 39 | public void extractNamesSortedByLastname() { 40 | assertThat(FilteringAndMapping.extractNamesSortedByLastname(persons.subList(0, 6)), 41 | contains("Michael Abrahams", "Chris Cross", "John Dole", "Jane Jungle", "Pete Power", "Mary Smith")); 42 | } 43 | 44 | @Test 45 | public void extractAdultWomen() { 46 | // Yes, I know, this test is time-dependent and will break in a few months/years... 47 | assertThat(FilteringAndMapping.extractAdultWomen(persons), contains(persons.get(0), persons.get(1))); 48 | } 49 | 50 | @Test 51 | public void extractFirstnamesWhereLastnameStartsWith() { 52 | assertThat(FilteringAndMapping.extractFirstnamesWhereLastnameStartsWith(persons, "S"), contains("Maggie", "Mary")); 53 | } 54 | 55 | @Test 56 | public void testExtractAllProducts() throws Exception { 57 | MatcherAssert.assertThat(FilteringAndMapping.extractAllProducts(invoices), 58 | containsInAnyOrder("Beer", "Burger", "Corn Flakes", "Chips", "Coke", "Cake", "Left-Handed Scissors")); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/de/codecentric/java8examples/timeapi/TimeApiTest.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.java8examples.timeapi; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import java.text.DateFormat; 8 | import java.text.ParseException; 9 | import java.text.SimpleDateFormat; 10 | import java.time.LocalDate; 11 | import java.time.LocalDateTime; 12 | import java.time.Month; 13 | import java.time.Period; 14 | import java.time.ZoneId; 15 | import java.time.ZonedDateTime; 16 | import java.time.chrono.ThaiBuddhistDate; 17 | import java.time.format.DateTimeFormatter; 18 | import java.time.temporal.ChronoField; 19 | import java.util.Calendar; 20 | import java.util.Date; 21 | import java.util.GregorianCalendar; 22 | import java.util.TimeZone; 23 | 24 | import org.joda.time.DateTime; 25 | import org.joda.time.DateTimeFieldType; 26 | import org.joda.time.DateTimeZone; 27 | import org.joda.time.Interval; 28 | import org.joda.time.chrono.BuddhistChronology; 29 | import org.joda.time.format.DateTimeFormat; 30 | import org.junit.Test; 31 | 32 | import sun.util.BuddhistCalendar; 33 | 34 | /** 35 | * Why JSR-310 isn't Joda-Time
36 | * nice presentation (JUG Ukraine)
37 | * project page
38 | * Java-Doc
39 | * 40 | *
41 | *

Joda

42 | *
    43 | *
  • Instant - Milliseconds from 1970-01-01T00:00Z
  • 44 | *
  • Partial - Only partially represent a date or time in the datetime continuum.
  • 45 | *
  • Interval - Time between two instants (TimeZone aware)
  • 46 | *
  • Duration - No TimeZone aware or Chronology, just millis
  • 47 | *
  • Period - No TimeZone aware or Chronology, defined in terms of fields, for ex: 1 day 5 hrs. Differs from Duration in that it is inexact in terms of 48 | * millis.
  • 49 | *
  • Chronology - Pluggable chronologies. A Chronology is obtained and used in Joda-Time as a singleton (Iso is default)
  • 50 | *
  • TimeZone - Bazed on TZ(Olson). Frquently updatable. You can also update whenever required.
  • 51 | *
52 | *
    53 | *
  • LocalDate - just date w/o tz, e.g. 2013-02-22
  • 54 | *
  • LocalTime - just time w/o tz, e.g. 10:15:30
  • 55 | *
  • LocalDateTime - date and time w/o offset, e.g. 2013-02-22 T10:15:30
  • 56 | *
  • DateTime - date with time and DateTimeZone
  • 57 | *
58 | * 59 | *

JSR-310

60 | *
    61 | *
  • Instant - An instant is a specific point on a discretized timeline. An example of an instant is "January 7, 2008, at time 23:00:00.0 UTC."
  • 62 | *
  • 63 | * Partial (for example MonthDay, YearMonth)- A partial is an indication of date or time that is not sufficient to specify a specific, unique point on the 64 | * timeline. For example, "June 7" is a partial, because it specifies a month and day, but it does not specify a year or time of day.
  • 65 | *
  • Interval - Was planned, but removed later - NOT EXISTING
  • 66 | *
  • Duration - A duration represents a length of elapsed time, defined to the nanosecond level; for example, "100,000 nanoseconds" is a duration.
  • 67 | *
  • Period - Like durations, periods represent a length of elapsed time. Examples of periods are "4 years, 8 days," and "1 hour." As shown by these examples, 68 | * periods are defined using calendar fields (years, days, hours, etc.), rather than by an exact number of nanoseconds.
  • 69 | *
  • Chronology - A calendar system, used to organize and identify dates. (Iso is default for LocalDate)
  • 70 | *
71 | *
    72 | *
  • LocalDate - just date w/o tz, e.g. 2013-02-22
  • 73 | *
  • LocalTime - just time w/o tz, e.g. 10:15:30
  • 74 | *
  • LocalDateTime - date and time w/o offset, e.g. 2013-02-22 T10:15:30
  • 75 | *
  • ZoneOffset - offset against UTC (positive or negative), e.g. +05:00, +01:00, -02:00, +04:30, Z, CEST, UTC, GMT
  • 76 | *
  • OffsetDate - date w/o time but with offset, e.g. 2013-12-03+02:00
  • 77 | *
  • OffsetTime - time w/o date but with offset, e.g. 10:15:30+02:00
  • 78 | *
  • OffsetDateTime - date with time and offset
  • 79 | *
  • ZonedDateTime - date with time and Zone
  • 80 | *
81 | * 82 | * @author daniel.reuter 83 | */ 84 | public class TimeApiTest { 85 | 86 | @Test 87 | public void timeBetweenToDates() { 88 | // JSR-310 89 | LocalDate birthday = LocalDate.of(1981, 9, 3); 90 | Period between = Period.between(birthday, LocalDate.now()); 91 | System.out.println(between.getYears() + " Years " + between.getMonths() + " Months " + between.getDays() + " Days."); 92 | assertEquals(32, between.getYears()); 93 | assertEquals(2, between.getMonths()); 94 | 95 | // Joda 96 | org.joda.time.LocalDate birthdayJoda = new org.joda.time.LocalDate(1981, 9, 3); 97 | org.joda.time.Period betweenJoda = new org.joda.time.Period(birthdayJoda, org.joda.time.LocalDate.now()); 98 | System.out.println(betweenJoda.getYears() + " Years " + betweenJoda.getMonths() + " Months " + betweenJoda.getDays() + " Days."); 99 | assertEquals(32, betweenJoda.getYears()); 100 | assertEquals(2, betweenJoda.getMonths()); 101 | 102 | // Date & Calendar 103 | Calendar birthdayCalendar = Calendar.getInstance(); 104 | birthdayCalendar.set(1981, Calendar.SEPTEMBER, 3); 105 | int diffYears = Calendar.getInstance().get(Calendar.YEAR) - birthdayCalendar.get(Calendar.YEAR); 106 | int diffMonth = Calendar.getInstance().get(Calendar.MONTH) - birthdayCalendar.get(Calendar.MONTH); 107 | assertEquals(32, diffYears); 108 | assertEquals(2, diffMonth); 109 | } 110 | 111 | @Test 112 | public void checkDateIsInInterval() { 113 | // JSR-310 - No support - manually 114 | LocalDate birthday = LocalDate.of(1981, 9, 3); 115 | LocalDate now = LocalDate.now(); 116 | assertFalse(checkInterval(birthday, now, LocalDate.of(1980, 1, 1))); 117 | assertTrue(checkInterval(birthday, now, LocalDate.of(1985, 1, 1))); 118 | 119 | // Joda 120 | Interval interval = new Interval(new DateTime(1981, 9, 3, 12, 0), DateTime.now()); 121 | assertFalse(interval.contains(new DateTime(1980, 1, 1, 12, 0))); 122 | assertTrue(interval.contains(new DateTime(1985, 1, 1, 12, 0))); 123 | 124 | // Date & Calendar 125 | Calendar birthdayCalendar = Calendar.getInstance(); 126 | birthdayCalendar.set(1981, Calendar.SEPTEMBER, 3); 127 | Calendar nowCalendar = Calendar.getInstance(); 128 | assertFalse(checkInterval(birthdayCalendar, nowCalendar, new GregorianCalendar(1980, 1, 1))); 129 | assertTrue(checkInterval(birthdayCalendar, nowCalendar, new GregorianCalendar(1985, 1, 1))); 130 | 131 | } 132 | 133 | private boolean checkInterval(LocalDate from, LocalDate to, LocalDate date) { 134 | return from.isBefore(date) && to.isAfter(date); 135 | } 136 | 137 | private boolean checkInterval(Calendar from, Calendar to, Calendar date) { 138 | return date.getTime().after(from.getTime()) && date.getTime().before(to.getTime()); 139 | } 140 | 141 | @Test 142 | public void calculate5Years() { 143 | // JSR-310 144 | LocalDate birthday = LocalDate.of(1981, 9, 3); 145 | LocalDate birthdayPlus = birthday.plusYears(10); 146 | assertEquals(birthdayPlus.getDayOfMonth(), 3); 147 | assertEquals(birthdayPlus.getMonth(), Month.SEPTEMBER); 148 | assertEquals(birthdayPlus.getYear(), 1991); 149 | 150 | // Joda 151 | org.joda.time.LocalDate birthdayJoda = new org.joda.time.LocalDate(1981, 9, 3); 152 | org.joda.time.LocalDate birthdayPlusJoda = birthdayJoda.plusYears(10); 153 | assertEquals(birthdayPlusJoda.getDayOfMonth(), 3); 154 | assertEquals(birthdayPlusJoda.getMonthOfYear(), 9); 155 | assertEquals(birthdayPlusJoda.getYear(), 1991); 156 | 157 | // Date & Calendar 158 | Calendar birthdayCalendar = Calendar.getInstance(); 159 | birthdayCalendar.set(1981, Calendar.SEPTEMBER, 3); 160 | birthdayCalendar.add(Calendar.YEAR,10); 161 | assertEquals(birthdayCalendar.get(Calendar.DAY_OF_MONTH), 3); 162 | // !! 0-based 163 | assertEquals(birthdayCalendar.get(Calendar.MONTH), Calendar.SEPTEMBER); 164 | assertEquals(birthdayCalendar.get(Calendar.YEAR), 1991); 165 | } 166 | 167 | @Test 168 | public void chronologies() { 169 | // JSR-310 170 | ThaiBuddhistDate thaiBuddhistDate = ThaiBuddhistDate.now(); 171 | int thaiYear = thaiBuddhistDate.get(ChronoField.YEAR); 172 | LocalDate isoDate = LocalDate.now(); 173 | int isoYear = isoDate.get(ChronoField.YEAR); 174 | assertEquals(thaiYear, isoYear + 543); 175 | 176 | // Joda 177 | org.joda.time.LocalDate buddhistDateJoda = new org.joda.time.LocalDate(BuddhistChronology.getInstance()); 178 | int buddhistYearJoda = buddhistDateJoda.get(DateTimeFieldType.year()); 179 | org.joda.time.LocalDate dateJoda = new org.joda.time.LocalDate(); 180 | int yearJoda = dateJoda.get(DateTimeFieldType.year()); 181 | assertEquals(buddhistYearJoda, yearJoda + 543); 182 | 183 | // Date & Calendar 184 | BuddhistCalendar buddhistCalendar = new BuddhistCalendar(); 185 | int buddhistYearCalendar = buddhistCalendar.get(BuddhistCalendar.YEAR); 186 | Calendar calendar = Calendar.getInstance(); 187 | int yearCalendar = calendar.get(Calendar.YEAR); 188 | assertEquals(buddhistYearCalendar, yearCalendar + 543); 189 | } 190 | 191 | 192 | @Test 193 | public void timezoneAndOffset() throws InterruptedException { 194 | // JSR-310 195 | LocalDateTime dateTime = LocalDateTime.now(); 196 | ZonedDateTime zonedDateTimeEurope = ZonedDateTime.of(dateTime, ZoneId.systemDefault()); 197 | ZonedDateTime zonedDateTimeUTC = ZonedDateTime.of(dateTime, ZoneId.of("UTC")); 198 | 199 | assertEquals(60 * 60 * 1, zonedDateTimeEurope.getOffset().getTotalSeconds()); 200 | assertEquals("Europe/Berlin", zonedDateTimeEurope.getZone().getId()); 201 | //!! 202 | assertTrue(zonedDateTimeEurope.isBefore(zonedDateTimeUTC)); 203 | assertFalse(zonedDateTimeEurope.isAfter(zonedDateTimeUTC)); 204 | assertFalse(zonedDateTimeEurope.isEqual(zonedDateTimeUTC)); 205 | 206 | // Joda 207 | Date date = new Date(); 208 | DateTime dateTimeEuropeJoda = new DateTime(date.getTime(),DateTimeZone.getDefault()); 209 | DateTime dateTimeUTCJoda = new DateTime(date.getTime(),DateTimeZone.UTC); 210 | 211 | assertEquals(60 * 60 * 1 * 1000, dateTimeEuropeJoda.getZone().getOffset(dateTimeEuropeJoda.getMillis())); 212 | assertEquals("Europe/Berlin", dateTimeEuropeJoda.getZone().getID()); 213 | //!! 214 | assertFalse(dateTimeEuropeJoda.isBefore(dateTimeUTCJoda)); 215 | assertFalse(dateTimeEuropeJoda.isAfter(dateTimeUTCJoda)); 216 | assertTrue(dateTimeEuropeJoda.isEqual(dateTimeUTCJoda)); 217 | 218 | // Date & Calendar 219 | Calendar calendarEurope = Calendar.getInstance(); 220 | calendarEurope.setTime(date); 221 | Calendar calendarUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 222 | calendarEurope.setTime(date); 223 | assertFalse(calendarEurope.getTime().before(calendarUTC.getTime())); 224 | assertFalse(calendarEurope.getTime().after(calendarUTC.getTime())); 225 | assertTrue(calendarEurope.getTime().equals(calendarUTC.getTime())); 226 | } 227 | 228 | @Test 229 | public void parsingAndFormatting() { 230 | // JSR-310 231 | DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd.MM.yyyy"); 232 | LocalDate parsedLocalDate = LocalDate.parse("03.09.1981", fmt); 233 | assertEquals(LocalDate.of(1981, 9, 3), parsedLocalDate); 234 | assertEquals("03.09.1981", fmt.format(LocalDate.of(1981, 9, 3))); 235 | 236 | // Joda 237 | org.joda.time.format.DateTimeFormatter fmtJoda = DateTimeFormat.forPattern("dd.MM.yyyy"); 238 | org.joda.time.LocalDate parsedDateJoda = org.joda.time.LocalDate.parse("03.09.1981", fmtJoda); 239 | assertEquals(new org.joda.time.LocalDate(1981, 9, 3), parsedDateJoda); 240 | assertEquals("03.09.1981", fmtJoda.print(new org.joda.time.LocalDate(1981, 9, 3))); 241 | 242 | // Date & Calendar 243 | DateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy"); 244 | Date parsedDate = null; 245 | try { 246 | parsedDate= dateFormat.parse("03.09.1981"); 247 | } catch (ParseException e) { 248 | // handle 249 | } 250 | assertEquals(new GregorianCalendar(1981, Calendar.SEPTEMBER,3).getTime(), parsedDate); 251 | assertEquals("03.09.1981", dateFormat.format(new GregorianCalendar(1981, Calendar.SEPTEMBER,3).getTime())); 252 | } 253 | 254 | } --------------------------------------------------------------------------------