Type Classes Are Coming to Java. What the Valhalla Prototype Actually Means
In late 2025, a quiet but consequential prototype landed in the Valhalla repository. If it ever ships, the way Java handles generics will never look quite the same.
Most improvements to Java arrive with a press release, a JEP number, and a timeline. This one arrived differently — as an experimental prototype quietly pushed by Project Valhalla engineer Maurizio Cimadamore in late 2025, later presented by Brian Goetz at the JVM Language Summit (JVMLS). The topic? Type classes — a concept so closely associated with Haskell that many Java developers assumed it would never come anywhere near the JDK.
And yet, here we are. Furthermore, this isn’t vaporware. It’s a real prototype you can explore today, and it points to a genuine long-term direction for how Java handles abstraction over types — including primitive types like int and long. So let’s walk through what type classes actually are, why Java has needed them for so long, and what this prototype changes — in plain language, without the academic fog.
1. What Exactly Are Type Classes?
Before diving into Java specifics, it’s worth getting the concept right, because the name is genuinely confusing at first. A type class is not a class in the object-oriented sense. Think of it, instead, as a contract — a named set of operations that a type agrees to support.
In Haskell, the Num type class describes types that support numeric operations like addition and multiplication. Any type that provides those operations can be used wherever Num is expected. In Rust, the same idea is called a trait. The key insight is this: you define the behavior separately from the type itself. A type can retroactively opt into a type class without changing its source code at all.
“A type class says: here is a named set of capabilities. Any type can provide an implementation of those capabilities — even types you don’t own.”— Paraphrase of Brian Goetz’s framing, JVMLS 2025
This is fundamentally different from how Java interfaces work today. With interfaces, the type itself must declare that it implements the interface. You cannot, for instance, make java.lang.Integer retroactively implement a new interface you just invented — not without modifying Integer‘s source. Type classes remove that constraint entirely.
2. The Long-Standing Generics Problem in Java
2.1 Why You Can’t Write Generic Math Today
Here is a frustration every Java developer has hit at some point. Suppose you want to write a method that sums a list of numbers — generically, so it works with int, double, BigDecimal, or a custom monetary type. In Java today, you genuinely cannot do this cleanly. You could write it for double, or for BigDecimal, but you cannot write one version that works for both primitives and objects without resorting to autoboxing overhead or code duplication.
The reason is twofold. First, int is a primitive — it doesn’t participate in the type hierarchy at all. Second, even for object types, there is no common interface for “types that support addition.” Number exists, but it doesn’t define +. Comparable exists, but that’s only about ordering. There is simply no standard way to say “this type supports arithmetic” in a way the compiler can verify and optimise.
As a result, the JDK itself contains this problem baked in. Look at Stream versus IntStream, LongStream, and DoubleStream. That’s the same API written four times, because the type system couldn’t express the abstraction in a single, unified form.
The Cost of Missing Abstraction: JDK Stream API Duplication

3. Enter Project Valhalla — and the 2025 Prototype
3.1 A Brief Valhalla Recap
Project Valhalla has been running since 2014 with a core goal: make the JVM’s type system more expressive, especially around value types. The project has made significant progress delivering value classes (formerly “value types”) — objects that behave like primitives, with identity-free semantics — through proposals like JEP 401. However, making value classes work well with generics exposed the deeper problem: you still can’t write a single generic algorithm that works uniformly over int, a value class, and a regular object type. That’s precisely where type classes come in.
3.2 What Cimadamore’s Prototype Introduces
In the experimental Valhalla branch, the prototype introduces a new language construct — currently called a type class — that lets you declare a named set of operations. You can then write generic code parameterised not just over a type, but over an implementation of those operations for that type. Critically, the implementation lives outside the type itself.
Consequently, you could define a type class for “numeric addition,” provide an implementation for int, another for BigDecimal, and a third for your own custom Money class — all without modifying int, BigDecimal, or Money. Then a single generic sum() method could work cleanly across all three, with no boxing involved.
Key Point
The prototype lives in the experimental Valhalla repository and is not on any official JDK release track. It is exploratory work — the goal is to gather community feedback and validate the design direction, not ship in Java 25 or 26.
3.3 Checking Out the Prototype Yourself
If you’d like to explore the prototype directly, the Valhalla source is hosted on the OpenJDK infrastructure. The commands below clone the experimental branch and build a local JDK — you’ll need a recent JDK bootstrap build and common build dependencies (make, gcc/clang) installed first. This runs on Linux and macOS.
Terminal — Clone & build the Valhalla experimental branch
# 1. Clone the Valhalla source (shallow clone to save time) git clone --depth 1 https://github.com/openjdk/valhalla.git cd valhalla # 2. Check out the type-class experimental branch git fetch origin git checkout -b type-classes origin/type-classes 2>/dev/null || git checkout type-classes # 3. Configure the build (replace /path/to/boot-jdk with your JDK 21+ path) bash configure --with-boot-jdk=/path/to/boot-jdk # 4. Build (uses all available cores) make images # 5. Verify your new JDK ./build/*/images/jdk/bin/java --version
Of course, if building from source feels like too much overhead right now, the valhalla-dev mailing list contains detailed design discussions and code walk-throughs that are equally illuminating.
4. Why This Changes Things — Even If It Takes Years
4.1 Retroactive Polymorphism
The biggest shift is what you might call retroactive polymorphism. Today, Java’s polymorphism is entirely nominal — a type participates in an abstraction only if it explicitly declares that participation. Type classes make it structural in practice: any type can be made to participate, by anyone, at any time, without touching the original type’s source. This is how Rust traits work, and it’s one of the most powerful tools in modern systems programming.
Moreover, this doesn’t require breaking backward compatibility. Existing types like Integer or BigDecimal don’t need to change. A library author can provide witnesses — implementations of a type class for a given type — entirely separately.
4.2 Uniform Generics Over Primitives
Combined with Valhalla’s value types work, type classes open the door to writing generic code that doesn’t box primitives. Right now, List<int> is not legal Java — you have to write List<Integer>, which wraps every value into an object on the heap and costs you both memory and CPU. In a future world with type classes and specialised generics, a sum() method parameterised over a “numeric” type class could operate directly on int values on the stack. For numerical, scientific, and financial code, that is a meaningful performance win.
4.3 Library Design Gets Cleaner
Additionally, this would allow the JDK to retire API triplication. Rather than IntStream, LongStream, and DoubleStream existing as separate types, a future unified NumericStream<T> with a numeric type class constraint could serve all of them. The same pattern applies to Optional, Comparator, and a dozen other places where the JDK currently duplicates code at the API boundary.
5. How Java Compares to Other Languages
It’s useful to see where this proposal puts Java relative to languages that already have type classes or equivalent features. The table below gives a plain-language comparison across the most important dimensions.
| Language | Mechanism | Retroactive? | Works with Primitives? | Shipped? |
|---|---|---|---|---|
| Haskell | Type classes | Yes | Yes (native) | Since 1987 |
| Rust | Traits | Yes (orphan rules apply) | Yes (native) | Since 1.0 (2015) |
| Scala 3 | Given / Using | Yes | Partial (boxed on JVM) | Since Scala 3 (2021) |
| Swift | Protocols + extensions | Yes | Yes | Since Swift 1 (2014) |
| Kotlin | Context receivers (experimental) | Partial | With inline classes | Experimental as of 2025 |
| Java (today) | Interfaces only | No | No | — |
| Java (Valhalla prototype) | Type classes | Yes (target) | Yes (target) | Experimental only |
Timeline: When Did Each Language Get Type Class–Style Abstractions?

6. The Most Haskell Java Has Ever Been
The phrase circulating in the Java community — that this is “the most Haskell Java has ever been” — is evocative but worth unpacking carefully. Java is not becoming Haskell. There are no monads, no lazy evaluation, and no pure functional semantics on the horizon. However, there’s a real intellectual lineage here worth acknowledging.
Type classes originated in Haskell in the late 1980s as a solution to the same problem Java now faces: how do you write generic code over types that weren’t designed with your abstraction in mind? The answer Haskell gave — define the abstraction externally, provide implementations separately — is exactly the direction the Valhalla prototype is exploring. In that narrow but important sense, Java is borrowing a genuinely powerful idea. Furthermore, it’s doing so in a way that fits Java’s existing model: the JVM, backward compatibility, and the nominal type system remain intact. This isn’t a philosophical shift — it’s a targeted expansion of the abstraction toolkit.
7. What Comes Next — and What Doesn’t
It’s important to be realistic about timelines. Project Valhalla has been in flight for over a decade. Value types only recently reached the preview stage. Type classes are at the very beginning of the design pipeline — an experimental prototype, not a JEP, and certainly not a preview feature. There is no announced delivery vehicle or target release.
That said, the design intent is real. Brian Goetz presenting the concept at JVMLS signals that this is being seriously explored as part of the long-term language vision. The Valhalla project page and the valhalla-dev mailing list are the best places to follow progress and participate in the design conversation.
In the meantime, if you want to understand where this is heading, the best investment of your time is learning how Rust traits work in practice — they represent the shipped, production-proven version of the same idea. Scala 3’s given/using mechanism is also worth studying for its JVM-specific perspective. Both will make the eventual Java design much easier to understand when it arrives.
8. What We’ve Learned
Java has long had a generics gap: you can’t write a single generic algorithm that works cleanly across int, BigDecimal, and custom types — not without boxing overhead or code duplication. The root cause is that Java interfaces require explicit opt-in from the type itself, making retroactive abstraction impossible by design.
The experimental type classes prototype in the Valhalla repository, presented by Brian Goetz at JVMLS 2025, addresses this directly. It borrows the core insight from Haskell — define capabilities externally, provide implementations separately — and maps it onto the JVM’s type system. If the design matures, it would eliminate the JDK’s own API duplication (Stream / IntStream / LongStream), enable truly generic numerical code over primitives, and give library authors a cleaner compositional model across the board.
This is not shipping soon. But it is the most substantive rethinking of Java generics since type erasure, and it’s worth paying close attention to the conversation unfolding on the valhalla-dev list.

