New Way to Program in Java
λ
Java 8 New Features
- Default Methods
- Lambda Expression
- Fundamentals Of FP
- Optional API
- Stream API
Default Method
In Java 7, your API design can only like this
public interface Toxic {
void eatTamJai();
void surfGolden();
int findFriends();
int findGirlFriends();
}
public class Gap implements Toxic {
@Override
public void eatTamJai() {
System.out.println("Chicken rice noodle with medium spicy");
}
@Override
public void surfGolden() {
System.out.println("CD-ROM");
}
@Override
public int findFriends(){
return 0;
}
@Override
public int findGirlFriends(){
return 0;
}
}
Java 7
public class Peter implements Toxic {
@Override
public void eatTamJai() {
System.out.println("Beef rice noodle with ching soup");
}
@Override
public void surfGolden() {
System.out.println("Always LM");
}
@Override
public int findFriends(){
return 0;
}
@Override
public int findGirlFriends(){
return 0;
}
}
Problems???
Default Method
Java 7
public class Peter implements Toxic {
@Override
public void eatTamJai() {
System.out.println("Beef rice noodle with ching soup");
}
@Override
public void surfGolden() {
System.out.println("Always LM");
}
@Override
public int findFriends(){
return 0;
}
@Override
public int findGirlFriends(){
return 0;
}
}
A Toxic Person should
ALWAYS
Friends = 0 && GirlFriends = 0 !!!
In Real world, some properties/behaviours are always the same...
Default Method
Java 7
Default Method
In Java 8,
You can have a default implemetation in a interface.
- Setup the default behavior of the object
public interface Toxic {
void eatTamJai();
void surfGolden();
default int findFriends(){
return 0;
}
default int findGirlFriends(){
return 0;
}
}
public class Gap implements Toxic {
@Override
public void eatTamJai() {
System.out.println("Chicken rice noodle with medium spicy");
}
@Override
public void surfGolden() {
System.out.println("CD-ROM");
}
}
Java 8
Default Method
public interface Programmer {
default int findGirlFriends(){
return 0;
}
}
public class Gap implements Toxic, Programmer {
@Override
public void eatTamJai() {
System.out.println("Chicken rice noodle with medium spicy");
}
@Override
public void surfGolden() {
System.out.println("CD-ROM");
}
}
How about two interfaces have the same behaviours?
Java 8
Default Method
public interface Programmer {
default int findGirlFriends(){
return 0;
}
}
public class Gap implements Toxic, Programmer {
@Override
public void eatTamJai() {
System.out.println("Chicken rice noodle with medium spicy");
}
@Override
public void surfGolden() {
System.out.println("CD-ROM");
}
@Override
public int findGirlFriends() {
return Programmer.super.getGirlFriends();
}
}
Java 8
public interface Toxic {
void eatTamJai();
void surfGolden();
default int findFriends(){
return 0;
}
default int findGirlFriends(){
return 0;
}
}
Default Method
Java 8 Default Method vs Abstract Class
Java 7 Interface | Java 8 Interface | Abstract Class | |
---|---|---|---|
Method signatures | |||
Concrete methods | |||
Constructor |
|||
Member variables |
Java 8 New Features
- Default Methods
- Lambda Expression
- Fundamentals Of FP
- Optional API
- Stream API
Lambda Expression
λ
A lambda is just an anonymous function
Lambda Expression
λ
Java <= 7
- Strictly-OOP
- NO Anonymous Function (Popular to use Anonyomous class to substitute)
- Difficult to write functional style code
- Difficult to write high-order function
Lambda Expression
λ
With Java 8 Lambdas, we can ...
- Have Anonymous Function
- Cleaner representation compared with Anonymous class
- Code faster
- Produce Functional-style code
- Easier to write High-order functions ( Fundamental of Functional programming)
Lambda Expression
λ
//Lambda Expression syntax should like this
(arg1, arg2, ... ) -> { body }
// Or with Type
(type1 arg1, type2 arg2) -> { body }
Very Common Functional Interface in Java 8
Consumer, Predicate, Supplier, Function
Java 8 New Features
- Default Methods
- Lambda Expression
- Fundamentals Of FP
- Optional API
- Stream API
Fundamentals of FP
- Raise of Multi-core and distributed computing
- Simplicity, correctness and maintainability
- Commercial success by using functional language (ie. Twitter/Netflix - Scala, WhatsApp/LoL - Erlang)
Why more people talk about Functional Programming recently?
λ
Fundamentals of FP
- Avoids changing-state and mutable data
- Eliminating side effects (Write Pure Function)
Significant Conceptual Differences to Imperative Programming (i.e. OOP)
λ
Fundamentals of FP
Characteristic | Imperative | Functional |
---|---|---|
Programmer focus | How to track changes in state. | What transformations are required. |
State changes | Important | Non-existent. |
Primary flow control | Loops, conditionals, and function calls. | Function calls |
Primary manipulation | Instances of structures or classes. | Functions as first-class objects and data collections |
λ
public void addEvenUserIdToList(List<User> users, List<Integer> ids){
for(User user : users){
if(user.getId() % 2 == 0)
ids.add(user.getId());
}
}
public List<Integer> filterEvenAndTransformToUserId(List<User> users){
return users
.stream()
.map(User::getId)
.filter(id -> id % 2 == 0)
.collect(Collectors.toList());
}
λ
Imperative style
Functional style
Slide effects!!!! Not pure function
Collect as NEW objects!
No Side Effects => Pure function
Fundamentals of FP
Fundamentals of FP
Advantages of Pure Functions
- Increased readability and maintainability
- Easier to refactor, changes
- Easier testing and debugging
λ
Fundamentals of FP
High Order Function Example In Java 8
// f(x) = x + 1
Function<Integer,Integer> f = (x) -> x + 1;
// g(x) = x * x
Function<Integer,Integer> g = (x) -> x * x;
// h(x) = x * 2
Function<Integer,Integer> h = (x) -> x * 2;
// h(g(f(x))
Function<Integer,Integer> func = h.compose(g).compose(f);
// Integer to Double function
Function<Integer,Double> toDouble = Integer::doubleValue;
// toDouble(h(g(f(x)))
Function<Integer,Integer> funcWithLogging = func.andThen(toDouble);
λ
Fundamentals of FP
High Order Function Example In Java 8
@Test
public void test(){
// h(g(f(x)), where h(x) = x * 2 , g(x) = x * x , f(x) = x + 1;
int result = HighOrderFunctionExample.func.apply(1);
// h(g(f(1)) => h(g(2)) => h(4) => 8
assertEquals(result,8);
}
@Test
public void testWithLogging(){
double result = HighOrderFunctionExample.funcThenToDouble.apply(1);
assertEquals(result,8,0.001);
}
λ
Fundamentals of FP
Similar In Java 7 ?
public Integer f(Integer x) {
return x + 1;
}
public Integer g(Integer x) {
return x * x;
}
public Integer h(Integer x) {
return x * 2;
}
public Integer func(Integer x){
return h(g(f(x)));
}
public Double toDouble(Integer x){
return x.doubleValue();
}
public Double funcThenToDouble(Integer x){
return toDouble(func(x));
}
λ
public List<Integer> filterEvenAndTransformToUserId(List<User> users){
return users
.stream()
.map(User::getId)
.filter(id -> id % 2 == 0)
.collect(Collectors.toList());
}
Fundamentals of FP
Real World High order function In Java 8
λ
Predicate<Integer> filterEventNumber = x -> x % 2 == 0;
public List<Integer> filterEvenAndTransformToUserId2(List<User> users){
return users
.stream()
.map(User::getId)
.filter(filterEventNumber)
.collect(Collectors.toList());
}
Java 8 New Features
- Default Methods
- Lambda Expression
- Fundamentals Of FP
- Optional API
- Stream API
Optional API
- Replace null to avoid NullPointerException
- More meaningful than null
- Clearer code when doing Stream
Optional can ...
Optional API
Tony Hoare :
"I call it my billion-dollar mistake. It was the invention of the null reference in 1965."
"This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years"
Optional API
Before Java 8, Guava @Google has implemented Optional to avoid null
- null is unpleasantly ambiguous
- null gives no indication what a null value means
- it is a major source of confusion, difficult and weird bugs, and unpleasant ambiguities
Reason explained by Guava Team:
Optional API
Why Optional ?
- Increase readability
- idiot-proof-ness
- force you actively think about the absent case
Optional API
Replace null without rewriting root API
public User defaultValueUseCase1(){
User user = DB.findUserById(1);
if(user == null) user = new User(1,"a");
return user;
}
Use `Optional.ofNullable` wrap the object that maybe null
public User defaultValueUseCase1InOptional() {
return Optional.ofNullable(DB.findUserById(1))
.orElse(new User(1,"a"));
}
Use Case : Default Value
Use `Optional.orElse` to specify default value
Optional API
Replace null without rewriting root API
public User throwExceptionUseCase2() throws Exception {
User user = DB.findUserById(1);
if(user == null) throw new Exception();
return user;
}
Use `Optional.orElseThrow` to throw exception
public User throwExceptionUseCase2InOptional() throws Exception {
return Optional.ofNullable(DB.findUserById(1))
.orElseThrow(Exception::new);
}
Use Case : Exception Handling
Use `Optional.ofNullable` wrap the object that maybe null
Optional API
Replace null by rewriting root API
public class DB {
public static User findUserById(int id){
for(User u : users){
if(u.getId() == id)
return u;
}
return null;
}
//rewrite to return Optional
public static Optional<User> findUserByIdOpt(int id){
for(User u : users){
if(u.getId() == id)
return Optional.of(u);
}
return Optional.empty();
}
}
Use `Optional.of` wrap the object that must NOT null
Optional API
Streaming is very common
I need to find Object C
For example :
Look up Object C depends on Object B
Look up Object B depends on Object A
Finally, the look up sequence : A -> B -> C
Optional API
Java 7
public Country findUserCountry(User user){
if(user != null){
String countryId = user.getCountryId();
if(countryId != null){
return DB.findCountryById(countryId);
}
}
return null;
}
Real World Example,
User -> Country ID -> Country
Streaming is very common
Problems: Easy to miss, difficult to read
Optional API
Java 8
public Optional<Country> findUserCountryOpt(User user){
return Optional.ofNullable(user)
.map(User::getCountryId)
.map(DB::findCountryById);
}
Version 1, Return a Optional Value
Version 2, Throws Exception when not found
public Country findUserCountry(User user) throws Exception {
return Optional.ofNullable(user)
.map(User::getCountryId)
.map(DB::findCountryById)
.orElseThrow(Exception::new);
}
Streaming is very common
Optional API
Java 8
Other Useful API
Optional<Integer> optional = Optional.of(1);
// Check the value exist in the Optional
optional.isPresent(); // return true;
// print the value if it exist
optional.ifPresent( x -> System.out.println(x));
// Apply filter ( x = 0 )
optional.filter(x -> x == 0).isPresent(); // return false;
// Flat & Map
optional.flatMap((x) -> Optional.of( x * x ));
Optional API
When NOT to use Optional ?
Don't use it if you are really processing Huge DataSet!
Using Oracle JMH for micro-benchmark : 3ns slower
@Benchmark
public Integer vanillaNullSafeAndDefaultValue(){
if(a == null){
a = 1;
}
return a;
}
@Benchmark
public Integer nullSafeAndDefaultValue(){
return Optional.ofNullable(a).orElse(1);
}
Java 8 New Features
- Default Methods
- Lambda Expression
- Fundamentals Of FP
- Optional API
- Stream API
Stream API
What is Streams ?
- No Storage
- Designed for functional programming
- Lazy Operation
- Use ONLY once
Stream API
Two Type Operation
Data Source
Intermediate Operation
(filter/map/FlatMap/distinct/skip/sorted...)
Terminal Operation
(reduce/collect /forEach/...)
Stream API
Intermediate Operation is Lazy
Until
Terminal Operation is applied
Stream.of(5,4,2,1,3)
.filter(x -> x > 3)
.sorted()
.forEach(x -> System.out.println(x));
//Output
4
5
Lazy is Good
Stream API
Lazy is Good
public void run(){
List<Integer> list = Arrays.asList(1,2,3,4);
multipleTwo(filterEvenNumber(list)).forEach(System.out::println);
}
public List<Integer> filterEvenNumber(List<Integer> list){
List<Integer> tmp = new ArrayList<>();
for(Integer x : list){
System.out.println("iterating " + x);
if(x % 2 == 0){
tmp.add(x);
}
}
return tmp;
}
public List<Integer> multipleTwo(List<Integer> list){
List<Integer> tmp = new ArrayList<>();
for(Integer x : list){
System.out.println("iterating " + x);
tmp.add(x *x);
}
return tmp;
}
Java 7
//Output
iterating 1
iterating 2
iterating 3
iterating 4
iterating 2
iterating 4
4
16
iterated 6 times !!!
Stream API
Lazy is Good
public void run(){
List<Integer> list = Arrays.asList(1,2,3,4);
Stream<Integer> rawStream =
list
.stream()
.peek(x -> System.out.println("iterating " + x));
Stream<Integer> stream = multipleTwo(filterEvenNumber(rawStream));
stream.forEach(System.out::println);
}
public Stream<Integer> filterEvenNumber(Stream<Integer> list){
return list.filter(x -> x % 2 == 0);
}
public Stream<Integer> multipleTwo(Stream<Integer> list){
return list.map(x -> x * x);
}
Java 8
//Output
iterating 1
iterating 2
4
iterating 3
iterating 4
16
Iterate 4 times = Size of List !!
Stream API
Lazy is Good
Java 8
public void functionalStyleRun(){
List<Integer> list = Arrays.asList(1,2,3,4);
list
.stream()
.filter(x -> x % 2 == 0)
.map(x -> x * x)
.forEach(System.out::println);
}
Simplified Version with Anonyomus function
Predicate<Integer> evenFilter = x -> x % 2 == 0;
Function<Integer,Integer> mult2 = x -> x * x ;
public void functionalStyleRun(){
List<Integer> list = Arrays.asList(1,2,3,4);
list
.stream()
.filter(evenFilter)
.map(mult2)
.forEach(System.out::println);
}
Simplified Version with Decleared function
Stream API
Powerful API
public List<Integer> distinct(List<Integer> list){
return list
.stream()
.distinct()
.collect(Collectors.toList());
}
public Map<String,List<User>> listToMap(List<User> users){
return users
.stream()
.collect(Collectors.groupingBy(
User::getCountryId,
Collectors.toList()
));
}
Collectors.* are awesome
Stream API
Powerful API
Happy Java Life !!!
Question ?
You can found all the examples in the following repository
git clone https://github.com/gaplo917/java8-intro
// modify & run the test to learn more Java 8!!
New Way to Program in Java 8
By Gap撈Tech
New Way to Program in Java 8
Default Method, Lambda Expression, Functional programming style, Optional API, Stream API
- 637