Card 05/ 09
Builder in the Wild: Java's HttpRequest.newBuilder()
URI, headers, timeout and method are set one call at a time, then build() returns an immutable request.
Java's HTTP client, in java.net.http since Java 11, creates every request through a builder. HttpRequest.newBuilder() returns an HttpRequest.Builder, each call sets one part of the request, and build() returns an HttpRequest that has no setters at all. The listing builds a request without sending it, so it runs with no network.
import java.net.URI;
import java.net.http.HttpRequest;
import java.time.Duration;
public class BdHttpRequest {
public static void main(String[] args) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/orders"))
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(5))
.POST(HttpRequest.BodyPublishers.ofString("{\"id\":\"A-1042\"}"))
.build();
System.out.println(request);
System.out.println("method: " + request.method());
System.out.println("headers: " + request.headers().map());
System.out.println("timeout: " + request.timeout().orElseThrow());
try {
HttpRequest.newBuilder().header("Accept", "text/plain").build();
} catch (IllegalStateException error) {
System.out.println("refused: " + error.getMessage());
}
}
}https://api.example.com/orders POST
method: POST
headers: {Content-Type=[application/json]}
timeout: PT5S
refused: uri is nullEvery part is optional in the builder's calls and checked in build(). The method defaults to GET, the headers to none, and the timeout to none, so a request names only what differs. The URI has no default, and the second build() throws an IllegalStateException whose message, from OpenJDK 21, is uri is null.
What this case shares with the report is the shape that identifies the pattern: calls that each set one named part, often chained, and one final call that validates and returns the finished object. The JDK adds one thing the report does not need: the HttpRequest it returns is immutable, so a request handed to several threads cannot be changed by any of them. That is the strongest reason to use a builder in Java, where a class without setters has no other way to receive a dozen optional parts by name.
← → move through sections and cards