Skip to content
This repository was archived by the owner on Feb 10, 2021. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 51 additions & 15 deletions crazy-optionals/src/main/java/com/bobocode/CrazyOptionals.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import java.util.Optional;
import java.util.OptionalDouble;

import static java.util.Comparator.comparing;

public class CrazyOptionals {

/**
Expand All @@ -24,7 +26,7 @@ public class CrazyOptionals {
* @return optional object that holds text
*/
public static Optional<String> optionalOfString(@Nullable String text) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return Optional.ofNullable(text); // Optional.ofNullable() will use Optional.empty() if text is null
}

/**
Expand All @@ -34,7 +36,10 @@ public static Optional<String> optionalOfString(@Nullable String text) {
* @param amount money to deposit
*/
public static void deposit(AccountProvider accountProvider, BigDecimal amount) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
accountProvider.getAccount()
.ifPresent(account -> account.setBalance(account.getBalance().add(amount))); // instead of using if operator
// you can pass Consumer object that will be used in case Optional is not empty
// this approach is called declarative and is usually more precise
}

/**
Expand All @@ -44,7 +49,7 @@ public static void deposit(AccountProvider accountProvider, BigDecimal amount) {
* @return optional object that holds account
*/
public static Optional<Account> optionalOfAccount(@Nonnull Account account) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return Optional.of(account); // Optional.of() will throw NullPointerException if account is null
}

/**
Expand All @@ -56,7 +61,8 @@ public static Optional<Account> optionalOfAccount(@Nonnull Account account) {
* @return account from provider or defaultAccount
*/
public static Account getAccount(AccountProvider accountProvider, Account defaultAccount) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return accountProvider.getAccount()
.orElse(defaultAccount); // Optional#orElse() can be used to provide default value is case Optional is empty
}

/**
Expand All @@ -67,7 +73,10 @@ public static Account getAccount(AccountProvider accountProvider, Account defaul
* @param accountService
*/
public static void processAccount(AccountProvider accountProvider, AccountService accountService) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
accountProvider.getAccount()
.ifPresentOrElse(accountService::processAccount, accountService::processWithNoAccount);
// one more declarative substitution of if-else operator.
// Please note its parameters: Consumer and Runnable
}

/**
Expand All @@ -78,7 +87,12 @@ public static void processAccount(AccountProvider accountProvider, AccountServic
* @return provided or generated account
*/
public static Account getOrGenerateAccount(AccountProvider accountProvider) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return accountProvider.getAccount()
.orElseGet(Accounts::generateAccount); // functionally it works exactly the same as Optional#orElse()
// however it is based on lazy initialization using Supplier interface, which means that default value
// will not be computed (created) until Supplier#get() is called, which means it will be only computed
// when Optional is empty. This method should be used in favor of Optional#orElse() when the creation of default
// value requires additional resources
}

/**
Expand All @@ -88,7 +102,8 @@ public static Account getOrGenerateAccount(AccountProvider accountProvider) {
* @return optional balance
*/
public static Optional<BigDecimal> retrieveBalance(AccountProvider accountProvider) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return accountProvider.getAccount()
.map(Account::getBalance); // a null-safe mapping that allows you to go from Optional object to its field
}

/**
Expand All @@ -99,7 +114,9 @@ public static Optional<BigDecimal> retrieveBalance(AccountProvider accountProvid
* @return provided account
*/
public static Account getAccount(AccountProvider accountProvider) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return accountProvider.getAccount()
.orElseThrow(() -> new AccountNotFoundException("No Account provided!")); // in case Optional is empty
// it allows to throw a custom exception
}

/**
Expand All @@ -109,7 +126,9 @@ public static Account getAccount(AccountProvider accountProvider) {
* @return optional credit balance
*/
public static Optional<BigDecimal> retrieveCreditBalance(CreditAccountProvider accountProvider) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return accountProvider.getAccount()
.flatMap(CreditAccount::getCreditBalance); // in case your getter already return Optional, you cannot use
// Optional#map() because it will create Optional<Optional<Account>>. In this case Optional#flatMap() should be used
}


Expand All @@ -121,7 +140,10 @@ public static Optional<BigDecimal> retrieveCreditBalance(CreditAccountProvider a
* @return optional gmail account
*/
public static Optional<Account> retrieveAccountGmail(AccountProvider accountProvider) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return accountProvider.getAccount()
.filter(account -> account.getEmail().split("@")[1].equals("gmail.com"));
// in case you need to check if an Optional Account meets some criteria and return it or if it does not
// then return Optional.empty() and do that in a null-safe manner
}

/**
Expand All @@ -134,7 +156,9 @@ public static Optional<Account> retrieveAccountGmail(AccountProvider accountProv
* @return account got from either accountProvider or fallbackProvider
*/
public static Account getAccountWithFallback(AccountProvider accountProvider, AccountProvider fallbackProvider) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return accountProvider.getAccount()
.or(fallbackProvider::getAccount) // allows to use another Optional in case main Optional is empty
.orElseThrow(); // if both providers return Optional.empty() it throws NoSuchElementException
}

/**
Expand All @@ -145,7 +169,9 @@ public static Account getAccountWithFallback(AccountProvider accountProvider, Ac
* @return account with the highest balance
*/
public static Account getAccountWithMaxBalance(List<Account> accounts) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return accounts.stream()
.max(comparing(Account::getBalance)) // as you probably know Stream#min() and Stream#max() return Optional
.orElseThrow();
}

/**
Expand All @@ -155,7 +181,10 @@ public static Account getAccountWithMaxBalance(List<Account> accounts) {
* @return the lowest balance values
*/
public static OptionalDouble findMinBalanceValue(List<Account> accounts) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return accounts.stream()
.map(Account::getBalance) // map all stream accounts to balances
.mapToDouble(BigDecimal::doubleValue) // map all balances to primitive double values (returns DoubleStream)
.min(); // Optional API provides special classes for primitives as well
}

/**
Expand All @@ -165,7 +194,10 @@ public static OptionalDouble findMinBalanceValue(List<Account> accounts) {
* @param accountService
*/
public static void processAccountWithMaxBalance(List<Account> accounts, AccountService accountService) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
accounts.stream()
.max(comparing(Account::getBalance)) // returns Optional account
.ifPresent(accountService::processAccount); // declarative if statement and processing
// the last method requires Consumer<Account> as argument, it is implements using method reference
}

/**
Expand All @@ -175,7 +207,11 @@ public static void processAccountWithMaxBalance(List<Account> accounts, AccountS
* @return total credit balance
*/
public static double calculateTotalCreditBalance(List<CreditAccount> accounts) {
throw new UnsupportedOperationException("Some people say that method does not work until you implement it");
return accounts.stream()
.map(CreditAccount::getCreditBalance) // transforms each element of stream into Optional<BigDecimal>
.flatMap(Optional::stream) // uses special Optional#stream() to filter all elements that are empty
.mapToDouble(BigDecimal::doubleValue) // transform BigDecimal into primitive double (returns DoubleStream)
.sum(); // calculates a sum of primitive double
}
}