Exceptions, Generics, Streams, Lambdas, Enums, Interfaces
Table of Contents
- Exceptions β try / catch / throw / finally
- Interfaces
- Inheritance and extends
- Generics β
<T> - Enum β Named Constants
- Optional β dealing with maybe-null
- Streams and Lambda Expressions
- The Builder Pattern
- StringBuilder β efficient string building
- Varargs and Arrays
- 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
HotwExecutionAllDetailsInputclass β 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
@Builderannotation 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