java
44 lines · 7 steps
Loading typed config into a Java record
A record models server settings and a set of static factory methods parse them safely from a properties file.
Explained by
highlit
1public record ServerConfig(
2 String host,
3 int port,
4 Duration timeout,
5 boolean tlsEnabled,
6 List<String> allowedOrigins) {
7
8 public static ServerConfig load(Path file) {
9 Properties props = new Properties();
10 try (InputStream in = Files.newInputStream(file)) {
11 props.load(in);
12 } catch (IOException e) {
13 throw new UncheckedIOException("Failed to read config: " + file, e);
14 }
15 return fromProperties(props);
16 }
17
18 public static ServerConfig fromProperties(Properties props) {
19 return new ServerConfig(
20 require(props, "server.host"),
21 Integer.parseInt(require(props, "server.port")),
22 Duration.ofSeconds(Long.parseLong(props.getProperty("server.timeoutSeconds", "30"))),
23 Boolean.parseBoolean(props.getProperty("server.tls.enabled", "false")),
24 parseList(props.getProperty("server.allowedOrigins", "")));
25 }
26
27 private static String require(Properties props, String key) {
28 String value = props.getProperty(key);
29 if (value == null || value.isBlank()) {
30 throw new IllegalStateException("Missing required config key: " + key);
31 }
32 return value.trim();
33 }
34
35 private static List<String> parseList(String raw) {
36 if (raw.isBlank()) {
37 return List.of();
38 }
39 return Arrays.stream(raw.split(","))
40 .map(String::trim)
41 .filter(s -> !s.isEmpty())
42 .toList();
43 }
44}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Records give you an immutable, self-documenting data carrier with almost no boilerplate.
- 2Static factory methods keep parsing and validation logic beside the type they build.
- 3Fail loudly on missing required values but fall back to sensible defaults for optional ones.
Related explainers
typescript
import { Controller, Get, Query,
Validating paginated queries in NestJS
pagination
validation
pipes
Intermediate
7 steps
java
import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Map; import java.util.regex.Pattern;
Ranking the most frequent words in Java
streams
regex
grouping
Intermediate
7 steps
java
public final class HttpHeaders { private final NavigableMap<String, List<String>> values = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
A case-insensitive HTTP headers map in Java
case-insensitivity
multimap
immutability
Intermediate
7 steps
php
<?php namespace App\Reminders;
Scheduling reminders across time zones in PHP
timezones
datetime
immutability
Intermediate
7 steps
typescript
import { Controller, Post, UploadedFile,
Handling avatar uploads in NestJS
file-upload
validation
interceptors
Intermediate
7 steps
java
public final class IbanValidator { private static final Map<String, Integer> COUNTRY_LENGTHS = Map.ofEntries( Map.entry("DE", 22), Map.entry("FR", 27), Map.entry("GB", 22),
Validating IBANs with the mod-97 checksum
validation
checksum
modular-arithmetic
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/loading-typed-config-into-a-java-record-explained-java-d725/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.