Exploratory proposal: First-class namespace construct for data-oriented / functional Java

Øyvind Kvien oyvind at kvien.no
Fri Feb 13 13:11:01 UTC 2026


Dear Amber team,

I would like to explore the idea of introducing a first-class namespace
construct in Java, intended to support data-oriented and functional
programming styles without repurposing classes or interfaces as containers
for static members.

Motivation

In data-oriented and mostly functional Java codebases, it is common to
group:

   -

   Stateless utility functions
   -

   Related constants
   -

   Small domain records
   -

   Parsing and validation logic

Today, the idiomatic way to express this grouping is:

public final class Patient {

    private Patient() {}

    public static final String PATIENT_REFERENCE_PREFIX = "Patient/";
    public static final Pattern PATIENT_IDENTIFIER_PATTERN =
        Pattern.compile("^Patient/(.*)", Pattern.CASE_INSENSITIVE);

    public record PatientIdentifier(String id) {}

    public static PatientIdentifier getPatientIdentifier(@Nullable
Reference patientRef) {
        if (patientRef == null || patientRef.getReference() == null)
            return new PatientIdentifier(Str.EMPTY);

        var matcher =
PATIENT_IDENTIFIER_PATTERN.matcher(patientRef.getReference());
        return new PatientIdentifier(matcher.find() ? matcher.group(1)
: Str.EMPTY);
    }
}

This works, but it introduces conceptual and syntactic friction:

   -

   The type is not meant to be instantiated.
   -

   The constructor must be manually suppressed.
   -

   Everything must be explicitly marked static.
   -

   The type is not modeling an object or abstraction; it is modeling a
   namespace.

In practice, such classes act as packages within packages.

An alternative is to use an interface as a namespace:

public interface Patient {
    String PATIENT_REFERENCE_PREFIX = "Patient/";
    record PatientIdentifier(String id) {}
    static PatientIdentifier getPatientIdentifier(...) { ... }
}

However, interfaces are primarily intended to model polymorphic contracts
and substitution. Repurposing them as namespace containers can blur intent
and introduce conceptual confusion. The well-known “constant interface”
anti-pattern illustrates this discomfort.

In short, Java lacks a first-class way to express “this is a namespace for
related functions and data types.”

Design Goal

Provide a language-level construct that:

   -

   Represents a pure namespace (not a type with identity).
   -

   Cannot be instantiated or extended.
   -

   Groups related functions, constants, and nested types.
   -

   Supports access modifiers.
   -

   Avoids boilerplate such as private constructors and repeated static.
   -

   Preserves Java’s explicitness and readability.

Strawman Syntax

One possible direction:

public namespace Patient {

    String PATIENT_REFERENCE_PREFIX = "Patient/";
    Pattern PATIENT_IDENTIFIER_PATTERN =
        Pattern.compile("^Patient/(.*)", Pattern.CASE_INSENSITIVE);

    record PatientIdentifier(String id) {}

    PatientIdentifier getPatientIdentifier(@Nullable Reference patientRef) {
        ...
    }
}

Semantics (strawman)

   -

   namespace introduces a named scope at top level.
   -

   Members are implicitly static.
   -

   Fields are implicitly static final unless specified otherwise.
   -

   Nested types are implicitly static.
   -

   The namespace itself:
   -

      Cannot be instantiated.
      -

      Cannot implement or extend anything.
      -

      Cannot declare instance state.
      -

   Initialization semantics follow class static initialization rules.
   -

   At the bytecode level, the namespace may be compiled to a synthetic
   final class, preserving JVM compatibility.

Why Not Just Use Classes?

Using final classes with private constructors is serviceable but
semantically misleading:

   -

   A class suggests instantiability, inheritance relationships, or type
   abstraction.
   -

   Namespace-only classes are often flagged as utility classes.
   -

   The pattern is common enough that it arguably deserves first-class
   support.

Why Not Just Use Interfaces?

Interfaces are designed primarily for polymorphic abstraction. Using them
as namespace containers:

   -

   Conflates two distinct concepts (contract vs grouping).
   -

   Introduces ambiguity in API design intent.
   -

   Encourages patterns that may confuse less experienced developers.

Providing a dedicated construct allows interfaces to remain focused on
substitution and abstraction.

Interaction With Existing Features

Questions for exploration include:

   -

   Should namespace members require explicit static, or be implicitly
   static?
   -

   Should access modifiers default to the namespace’s modifier?
   -

   How do annotations apply to the namespace?
   -

   Should nested namespaces be allowed?
   -

   How does reflection expose namespaces?
   -

   How should Javadoc render them?

A minimal version could require explicit modifiers and treat namespaces as
a restricted form of top-level type compiled to a synthetic final class.

Summary

As Java evolves toward stronger support for data-oriented programming
(records, pattern matching, etc.), it may be worth revisiting how we
express stateless domain logic and function groupings.

A first-class namespace construct could:

   -

   Reduce boilerplate.
   -

   Clarify intent.
   -

   Preserve the role of classes and interfaces.
   -

   Improve expressiveness for functional-style Java.

I would be interested in feedback on:

   1.

   Whether this problem is considered significant enough.
   2.

   Whether a namespace construct fits Java’s philosophy.
   3.

   Whether there are smaller or more incremental ways to address the issue.

Best regards,
Øyvind Kvien
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20260213/d8a95146/attachment-0001.htm>


More information about the amber-dev mailing list