Exceptions, Generics, Streams, Lambdas, Enums, Interfaces


Table of Contents

  1. Exceptions β€” try / catch / throw / finally
  2. Interfaces
  3. Inheritance and extends
  4. Generics β€” <T>
  5. Enum β€” Named Constants
  6. Optional β€” dealing with maybe-null
  7. Streams and Lambda Expressions
  8. The Builder Pattern
  9. StringBuilder β€” efficient string building
  10. Varargs and Arrays
  11. Math utilities

1. Exceptions

An exception is an error that happens at runtime (while the program is running). Java forces you to handle them.

Types of exceptions:

Throwable
β”œβ”€β”€ Error              (serious system errors β€” don't catch these)
└── Exception
    β”œβ”€β”€ Checked        (must handle in code β€” compiler forces you)
    β”‚   β”œβ”€β”€ IOException
    β”‚   └── ParseException
    └── RuntimeException (unchecked β€” optional to handle)
        β”œβ”€β”€ NullPointerException
        β”œβ”€β”€ IllegalArgumentException
        β”œβ”€β”€ IllegalStateException
        └── ArrayIndexOutOfBoundsException

try-catch-finally:

try {
    // Code that might fail
    Fleet fleet = callGetFleet(fleetId);   // might throw exception
    int hosts = fleet.getHostCount();       // fleet could be null!
    
} catch (NullPointerException e) {
    // Handle specific exception
    System.out.println("Fleet was null: " + e.getMessage());
    
} catch (Exception e) {
    // Handle any other exception (catch-all)
    System.out.println("Some error: " + e);
    e.printStackTrace();   // print the full error trace
    
} finally {
    // This ALWAYS runs, whether error happened or not
    // Used for cleanup (close connections, etc.)
    System.out.println("This always executes");
}

throw β€” create and send an exception:

public void validateFleet(Fleet fleet) {
    if (fleet == null) {
        // Create and throw an exception
        throw new IllegalArgumentException("Fleet cannot be null!");
        // Execution stops here β€” jumps to catch block
    }
}

Multiple catch blocks:

try {
    String result = callAPI();
    int parsed = Integer.parseInt(result);  // might fail if not a number
    
} catch (NumberFormatException e) {
    // Specific to number parsing failure
    logger.log("Not a valid number");
    
} catch (Exception e) {
    // All other errors
    logger.log("Unexpected error: " + e.getMessage());
    e.printStackTrace();
}

From HOTW code β€” getResult() method:

private HOTWHelperModel getResult(String fleetId, String eventId, String runId, String versionId)
    throws Exception {     // ← declares it CAN throw Exception
  try {
    // ... lots of work ...
    return hotwHelperModel;
    
  } catch (Exception e) {
    e.printStackTrace();
    throw e;               // re-throw it so caller knows it failed
    
  } finally {
    // Always runs even if exception happened:
    sqs.sendMessage(messageRequest);    // send SQS message regardless
    this.publishToMilestoneSNSTopic(fleetId, eventId);  // notify milestone
  }
}

The finally block ensures the SQS message is ALWAYS sent, even if the HOTW workflow failed.


2. Interfaces

An interface is a contract β€” it says β€œany class implementing me MUST have these methods.”

// Define an interface
public interface Printable {
    void print();          // no body β€” just the signature
    String getContent();   // just a signature
}

// A class implements the interface (promises to have those methods)
public class Report implements Printable {
    
    @Override          // ← annotation saying "this fulfills the interface"
    public void print() {
        System.out.println("Printing report...");
    }
    
    @Override
    public String getContent() {
        return "Report content here";
    }
}

Why interfaces matter in HOTW:

In HotwUpscalingHelper.java, there’s a field:

private final SleepStrategy sleepStrategy;

SleepStrategy is an interface. The actual implementation (what sleep actually does) can be swapped out β€” in production it really sleeps, in tests it does nothing. This is dependency injection β€” a key pattern in professional code.


3. Inheritance and extends

A class can extend another class, inheriting all its fields and methods.

// Parent class
public class Vehicle {
    String brand;
    int speed;
    
    public void move() {
        System.out.println("Moving at " + speed);
    }
}

// Child class β€” inherits everything from Vehicle
public class Car extends Vehicle {
    int doors;
    
    public void honk() {
        System.out.println("Beep beep!");
    }
}

// Usage:
Car car = new Car();
car.brand = "Toyota";   // inherited from Vehicle
car.speed = 60;         // inherited from Vehicle
car.doors = 4;          // Car's own field
car.move();             // inherited method from Vehicle
car.honk();             // Car's own method

@SuperBuilder in HOTW code:

// In HotwExecutionAllDetailsInput.java:
@SuperBuilder   // Lombok annotation for builders that support inheritance
public static class HotwExecutionDetails {
    // ...fields...
}

@SuperBuilder is needed when a class uses @Builder AND also extends another class. It makes the builder pattern work correctly with inheritance.


4. Generics

Generics let you write code that works with different types safely.

// WITHOUT generics β€” not type-safe:
List myList = new ArrayList();
myList.add("text");
myList.add(123);   // mixed types β€” BAD!
String s = (String) myList.get(1);  // ClassCastException at runtime!

// WITH generics β€” type-safe:
List<String> stringList = new ArrayList<>();   // <String> = only strings
stringList.add("FleetA");
stringList.add("FleetB");
// stringList.add(123);   // ❌ COMPILER ERROR β€” caught at compile time!
String s = stringList.get(0);   // no cast needed

Generic methods:

// TypeReference<T> β€” tells Jackson what type to deserialize to
// The <> means "any type goes here"
List<PreferredASG> list = JsonUtil.parseJson(response, new TypeReference<>() {});
//     ↑ type          ↑ JSON string   ↑ tells Jackson to make a List of PreferredASG

Common generic types:

Type Meaning
List<String> List containing only Strings
Map<String, Integer> Map with String keys and Integer values
Optional<Fleet> May or may not contain a Fleet
TypeReference<List<PreferredASG>> Type token for deserialization

5. Enum

An enum (enumeration) is a special class with a fixed set of named constants.

// Define an enum
public enum DayOfWeek {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// Using it:
DayOfWeek today = DayOfWeek.MONDAY;

if (today == DayOfWeek.SATURDAY || today == DayOfWeek.SUNDAY) {
    System.out.println("Weekend!");
}

Enum with fields and methods:

public enum StatusOfHOTW {
    SUCCESS("Success"),
    FAIL("Fail"),
    PARTIAL_SUCCESS("PartialSuccess");
    
    private final String status;   // each constant has a status string
    
    StatusOfHOTW(String value) {   // constructor
        this.status = value;
    }
    
    public String getStatus() {    // method to get the string value
        return status;
    }
}

Using the HOTW enum:

// Get the string value:
String s = StatusOfHOTW.SUCCESS.getStatus();   // "Success"
String f = StatusOfHOTW.FAIL.getStatus();      // "Fail"
String p = StatusOfHOTW.PARTIAL_SUCCESS.getStatus(); // "PartialSuccess"

// Compare:
if (hotwStatus.equals(StatusOfHOTW.SUCCESS.getStatus())) {
    // all good
}

This enum is defined INSIDE HotwExecutionAllDetailsInput class β€” it’s a nested enum.


6. Optional

Optional<T> is a container that may or may not hold a value. It’s a safer way to handle potentially-null results.

import java.util.Optional;

// Creating Optional:
Optional<String> withValue = Optional.of("Hello");
Optional<String> empty = Optional.empty();
Optional<String> maybe = Optional.ofNullable(getSomething());  // might be null

// Checking if present:
if (withValue.isPresent()) {
    String s = withValue.get();   // safe to call .get() now
}

// Or: get with a default:
String result = maybe.orElse("default value");

// Or: do something only if present:
maybe.ifPresent(val -> System.out.println("Got: " + val));

From HOTW code:

// HardwareOrdersUtil.java:
Optional<FleetProjectionItem> fleetProjectionForPeakOptional =
    Arrays.stream(fleet.getFleetProjection()).findFirst();
//                                             ↑ returns Optional<FleetProjectionItem>
//                                               because the array might be empty

if (!fleetProjectionForPeakOptional.isPresent()) {
    throw new IllegalStateException("Fleet projection array not present...");
}

Integer requiredDescaleHosts = fleetProjectionForPeakOptional.get().getRequiredDescaleHosts();
//                                                             ↑ safe to call .get() now

Also:

// HotwUpscalingHelper.java:
Optional<HostOrderStatus> hostOrderStatusesOptional =
    Arrays.stream(fleet.getHostOrderStatuses()).findFirst();

Integer totalPendingHost =
    getPendingApprovalHostOrders(hostOrderStatusesOptional.get().getHostsPendingApproval())
    + hostOrderStatusesOptional.get().getHostsPendingDelivery();

7. Streams and Lambda Expressions

Streams let you process collections in a functional way. Lambdas are short anonymous functions.

Lambda basics:

// Traditional way β€” anonymous inner class:
Comparator<String> comp = new Comparator<String>() {
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
};

// Lambda way β€” much shorter:
Comparator<String> comp = (a, b) -> a.compareTo(b);
//                         ↑ params   ↑ body

Stream operations:

List<String> fleets = List.of("FleetA-NA", "FleetB-EU", "FleetC-NA", "FleetD-EU");

// filter β€” keep only items matching a condition
List<String> naFleets = fleets.stream()
    .filter(fleet -> fleet.endsWith("-NA"))   // keep only NA fleets
    .collect(Collectors.toList());
// Result: ["FleetA-NA", "FleetC-NA"]

// map β€” transform each item
List<String> upperFleets = fleets.stream()
    .map(fleet -> fleet.toUpperCase())   // uppercase each
    .collect(Collectors.toList());

// mapToInt + sum β€” sum numbers
int totalHosts = hostCounts.stream()
    .mapToInt(value -> value)   // convert Integer to int
    .sum();

// allMatch β€” are ALL items true?
boolean allPassed = results.values().stream()
    .allMatch(Boolean.TRUE::equals);   // all values are true?

// anyMatch β€” is ANY item true?
boolean somePassed = results.values().stream()
    .anyMatch(Boolean.TRUE::equals);   // any value is true?

// collect(Collectors.toList()) β€” gather results into a List
List<String> failedAsgs = statusMap.entrySet().stream()
    .filter(entry -> !entry.getValue())     // entries where value is false
    .map(Map.Entry::getKey)                  // get just the key
    .collect(Collectors.toList());

flatMap β€” flatten nested collections:

// From TableUtil.java:
String queryString = queryParams.entrySet()
    .stream()
    .flatMap(entry -> {
        String key = entry.getKey();
        List<String> values = entry.getValue();
        return values.stream().map(value -> key + "=" + value);
        // FleetId=FleetA
        // FleetId=FleetB
        // tab=ec2
    })
    .collect(Collectors.joining("&"));
// Result: "FleetId=FleetA&FleetId=FleetB&tab=ec2"

sorted β€” sort a stream:

// From HardwareOrdersUtil.java β€” sort by start time descending:
.sorted(
    (override1, override2) ->
        DateTimeComparator.getInstance()
            .compare(
                override2.getCapacityOverride().getStartTime(),
                override1.getCapacityOverride().getStartTime()
            )
)

Method references (shorthand lambdas):

// Long lambda:
.map(entry -> entry.getKey())

// Method reference shorthand:
.map(Map.Entry::getKey)     // ::  means "use this method"

// Long lambda:
.allMatch(value -> Boolean.TRUE.equals(value))

// Method reference:
.allMatch(Boolean.TRUE::equals)

Real example from HOTW code β€” determining HOTW status:

String hotwExecutionStatus =
    asgHOTWExecutionStatus.values().stream()
        .allMatch(Boolean.TRUE::equals)          // ALL passed?
    ? StatusOfHOTW.SUCCESS.getStatus()           // β†’ "Success"
    : asgHOTWExecutionStatus.values().stream()
        .anyMatch(Boolean.TRUE::equals)          // SOME passed?
      ? StatusOfHOTW.PARTIAL_SUCCESS.getStatus() // β†’ "PartialSuccess"
      : StatusOfHOTW.FAIL.getStatus();           // β†’ "Fail"

Collectors.toMap β€” convert stream to Map:

// From HotwUpscalingHelper.java:
Map<String, Integer> asgToBauHostMap =
    Arrays.stream(autoScalingGroupDataModels)
        .filter(asg -> diffASGList.contains(asg.getAsgTag()))
        //      ↑ keep only ASGs in the diffASGList
        .collect(
            Collectors.toMap(
                AutoScalingGroupDataModel::getAsgTag,    // key = asgTag
                AutoScalingGroupDataModel::getHostCount  // value = hostCount
            )
        );

8. The Builder Pattern

The Builder pattern creates complex objects step by step, without needing a giant constructor.

Without builder (messy):

// 8-parameter constructor β€” which is which??
HotwExecutionAllDetailsInput input = new HotwExecutionAllDetailsInput(
    "FleetA-NA", "FORTRESSService", "PrimeDay26", 5,
    "Success", "No issues", executionDetails, capacityList
);

With builder (clean):

HotwExecutionAllDetailsInput input = HotwExecutionAllDetailsInput.builder()
    .fleetId("FleetA-NA")
    .serviceId("FORTRESSService")
    .eventId("PrimeDay26")
    .runId(5)
    .status("Success")
    .reasoning("No issues")
    .hotwExecutionDetails(executionDetails)
    .capacityOverrideDetails(capacityList)
    .build();           // ← creates the actual object

How builder works internally:

// What Lombok @Builder generates automatically:
public class HotwExecutionAllDetailsInput {
    private String fleetId;
    private String serviceId;
    // ... more fields ...
    
    // Lombok generates this inner class:
    public static class Builder {
        private String fleetId;
        private String serviceId;
        
        public Builder fleetId(String fleetId) {
            this.fleetId = fleetId;
            return this;   // ← returns 'this' so you can chain calls
        }
        
        public Builder serviceId(String serviceId) {
            this.serviceId = serviceId;
            return this;
        }
        
        public HotwExecutionAllDetailsInput build() {
            return new HotwExecutionAllDetailsInput(this);
        }
    }
    
    public static Builder builder() {   // entry point
        return new Builder();
    }
}

You don’t write this yourself! Lombok’s @Builder annotation generates it all automatically.


9. StringBuilder

Building strings by concatenating with + in a loop is very slow in Java.
StringBuilder is the efficient alternative.

// SLOW (creates new String object each time):
String result = "";
for (int i = 0; i < 1000; i++) {
    result = result + "data" + i;   // creates 1000 temporary strings!
}

// FAST (modifies one buffer):
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append("data").append(i);
}
String result = sb.toString();

StringBuilder methods:

StringBuilder sb = new StringBuilder();

sb.append("Hello");          // add text at end
sb.append(" World");
sb.insert(0, ">>> ");        // insert at position 0
sb.replace(4, 9, "Java");    // replace chars 4-9
sb.indexOf("World");         // find position of text
sb.toString();               // convert to regular String
int len = sb.length();       // current length

From HOTW code β€” building markdown table:

// TableUtil.java builds a markdown table in a SIM ticket:
StringBuilder tableBuilder = new StringBuilder();
tableBuilder.append(header).append("\n");

// Build column headers:  | Fleet Id | Peak TPM | BAU TPM | ...
StringJoiner headerJoiner = new StringJoiner(" | ", "| ", " |");
//                                            ↑separator ↑prefix ↑suffix
headerJoiner.add("Fleet Id").add("Peak TPM").add("BAU TPM");

tableBuilder.append(headerJoiner.toString()).append("\n");
// Result: | Fleet Id | Peak TPM | BAU TPM |

// Build separator row:   | --- | --- | --- |
StringJoiner separatorJoiner = new StringJoiner(" | ", "| ", " |");
for (int i = 0; i < columnCount; i++) {
    separatorJoiner.add("---");
}

This builds a markdown table like:

| Fleet Id | Peak TPM | BAU TPM | Required Hosts |
| --- | --- | --- | --- |
| [FleetA-NA](link) | 10000 | 5000 | 200 |

10. Varargs and Arrays

Arrays β€” fixed size collection:

// Array of integers
int[] numbers = {1, 2, 3, 4, 5};
int first = numbers[0];   // index 0 = first element
int len = numbers.length; // get length

// Array of objects
String[] names = new String[3];  // create array of size 3
names[0] = "Alice";
names[1] = "Bob";
names[2] = "Charlie";

Arrays utility class:

import java.util.Arrays;

// Convert array to List:
AutoScalingGroupDataModel[] asgArray = fleet.getFleetConfiguration().getAsgProperties();
List<AutoScalingGroupDataModel> asgList = List.of(asgArray);
// or:
List<AutoScalingGroupDataModel> asgList = Arrays.asList(asgArray);

// Stream an array:
Arrays.stream(fleet.getHostOrderStatuses())
    .findFirst()    // returns Optional<HostOrderStatus>

From HOTW code:

// FleetProjection is an ARRAY (fleet can have multiple projections):
FleetProjectionItem[] fleetProjections = fleet.getFleetProjection();
FleetProjectionItem first = fleetProjections[0];  // get first element

// Finding first element with Optional:
Optional<FleetProjectionItem> optional =
    Arrays.stream(fleet.getFleetProjection()).findFirst();

11. Math Utilities

// Math.ceil β€” round UP to nearest whole number
Math.ceil(3.2)  β†’ 4.0
Math.ceil(3.0)  β†’ 3.0
Math.ceil(3.9)  β†’ 4.0

// Math.floor β€” round DOWN to nearest whole number
Math.floor(3.9) β†’ 3.0
Math.floor(3.0) β†’ 3.0

// (int) cast β€” truncate to integer
(int) 3.9  β†’ 3   // just drops the decimal
(int) 3.1  β†’ 3

// Math.ceil + (int) cast β€” round up then make it an int:
int hosts = (int) Math.ceil(projectedHosts);  // e.g., 3.2 β†’ 4

From HOTW code:

// Required hosts calculation:
int reqHosts = (int) Math.ceil(fleet.getFleetProjection()[0].getProjectedHosts());
// Projected hosts is a Double β€” ceiling to be safe (never order less than needed)

// BAU TPM calculation (HardwareOrdersUtil.java):
private double calculateBauTpm(int totalInputTpm, double ctFactor, double bufferUsed) {
    return (double) totalInputTpm / (ctFactor * bufferUsed);
    // Peak TPM / (CT Factor Γ— Buffer) = BAU TPM
}

🎯 Layer 2 Summary

Concept What you learned
Exceptions try/catch/finally/throw β€” handle errors
Interfaces Contracts that classes must fulfill
Inheritance Child class gets parent’s features
Generics <T> Type-safe containers
Enum Fixed set of named constants with values
Optional Safe null handling β€” .isPresent(), .get(), .orElse()
Streams Functional processing of collections
Lambda -> Short anonymous functions
Method refs :: Even shorter lambdas
Builder Pattern Create objects step by step .field(val).build()
StringBuilder Efficient string building
Arrays Fixed-size collections, use Arrays.stream()
Math Math.ceil(), Math.floor(), (int) cast

β†’ Continue to Layer 3: Libraries & Annotations