JEP 532 in Java 27: Primitive Types in Patterns, instanceof, and switch (Fifth Preview)
-
Last Updated: October 5, 2026
-
By: javahandson
-
Series

JEP 532 brings primitive types in patterns to Java 27’s fifth preview. See how instanceof, switch, and record patterns now handle primitives safely.
Pattern matching in Java has grown step by step over many releases. Yet one gap stayed open for a long time. You could match reference types with ease, but primitive types in patterns were simply not allowed. JEP 532 closes that gap, and in Java 27 it arrives as the fifth preview.
In this guide, we will walk through this feature slowly and with small examples. You do not need to be a language expert to follow along. If you have written a few instanceof checks and a switch or two, you are ready.
Who gains the most from this change? Anyone who deals with numbers and ranges every day. Think of people working on money, metrics, sensor data, or parsing. For them, casting between int, long, double, and byte is a daily chore. This feature makes those casts safer and the code easier to read.
By the end of this guide, you will know:
instanceof, switch, and record patternsinstanceof can tell you whether a cast is safeLet us get started.
Let me begin with the plainest description I can give. The feature sounds fancy, but the idea behind it is small.
Before this feature, pattern matching worked only with reference types. For example, you could write obj instanceof String s, and Java would test the type and bind s for you. However, you could not write i instanceof byte b. Primitive types were blocked from patterns.
JEP 532 removes that wall. Now primitive types can appear in every pattern spot. As a result, three things change together:
instanceof operator works with primitive types like int, byte, and float.switch construct accepts primitive selectors and primitive case labels.In short, patterns now treat primitive and reference types the same way. That single sentence is the whole feature.
Java ships big language features slowly, and that is on purpose. A new feature first lands as a preview. During preview, the design is complete, but it is not permanent. The team watches real feedback and can still change things before the final version.
This feature has been in preview since Java 23. In each round so far, it was re-previewed without any change to its design. So the fifth preview in Java 27 is really a stability checkpoint. The design is holding steady and moving closer to a final release.
| 💡 Interview Insight Interviewers may ask why a feature sits in preview for several rounds. The reason is feedback plus safety. A preview feature has a complete design, but it is not final yet. JEP 532 was re-previewed without change across five rounds, which signals the design is now stable and almost done. |
Here is the full path this feature has taken across releases:
Five previews may sound like a lot. But primitive conversions are tricky, and the team wants the safety rules to be exactly right. For us, that caution is a good thing. It means fewer surprises when the feature finally goes permanent.
To really value JEP 532, let us look at the pain it removes. Once you feel the old pain, the new way makes instant sense.
Modern Java code leans heavily on pattern matching. We use it inside instanceof, inside switch, and inside record patterns. The pattern “check the type, then use the value” feels clean and natural.
Sadly, this power stopped at the primitive border. A switch could not take a long, float, double, or boolean selector in the new pattern style. Record patterns could not match a primitive component against a different primitive type. So developers had to drop back to older, clumsier code for these simple cases. The language felt uneven, and that bothered many people.
Why does an uneven language matter? Because our brains learn rules and then expect them to hold everywhere. When a rule works for String but breaks for int, you have to stop and remember the exception. Every exception is a small tax on your focus. JEP 532 removes one of those taxes, so the rules feel whole again.
Here is the real danger that JEP 532 targets. Casting one primitive to another can quietly lose data. Look at this classic example.
int i = 2345323; byte b = (byte) i; System.out.println(b);
You might expect an error or at least a warning. Instead, this prints 107. The large value overflowed the small byte range, and Java stayed silent. No exception, no complaint. Such a bug can hide in a codebase for months.
The same trap appears with floating-point values. A cast of 2.05f to an int drops the decimals and leaves 2. These silent conversions are a well-known source of elusive bugs. They are hard to spot in review, and harder to trace in production.
| 💡 Interview Insight A common question is what problem JEP 532 solves. Keep the answer concrete. Before, casting int to byte could silently lose data with no warning. Now instanceof can test whether the cast is safe first. Also mention that the compiler now blocks unsafe conversions inside a switch at compile time. |
Now let us see the feature in action. We will start with the simplest form and build up from there.
You can now place a primitive type right after instanceof. Java then checks whether the value fits that type without losing information.
float v = 1000.01f;
if (v instanceof float f) System.out.println("float value: " + f);
if (v instanceof double d) System.out.println("double value: " + d);
if (v instanceof int i) System.out.println("int value: " + i);The first two checks pass. The third one fails, because 1000.01f cannot become an int without dropping the decimal part. So nothing prints for the int line.
Here is the output you get:
float value: 1000.01 double value: 1000.010009765625
That double line looks strange at first. The tiny extra digits come from how floats and doubles store decimals in binary. It is expected behaviour, not a bug. Keep this in mind, because it surprises many beginners.
Think about it like this. A primitive instanceof asks one question. “Can this value become that type with no loss of information?” If the answer is yes, the check passes. If the answer is no, the check fails. That is the whole rule, and it keeps your casts honest.
This is a real shift in mindset. Earlier, instanceof was only about class hierarchies. You asked whether an object was a String or a Circle. Now the same keyword also asks about value ranges. So one familiar tool does double duty, and you have less new syntax to learn.
This part, in my view, is the best gift from JEP 532. It turns a dangerous cast into a safe, readable check. If you remember only one thing from this article, remember this section.

Earlier, to cast an int to a byte safely, you had to know the byte range by heart. Then you wrote the guard yourself.
if (i >= -128 && i <= 127) {
System.out.println((byte) i);
}This works correctly. However, it is easy to get wrong. You might type the wrong bound. You might copy the check from one place and forget to update it. Worst of all, you might skip the check entirely under deadline pressure.
Now instanceof performs that range check for you. The intent becomes obvious to anyone reading the code.
if (i instanceof byte) {
System.out.println((byte) i);
}Notice one detail here. In this form, instanceof only tests whether i fits in a byte. It does not perform the cast. So you still write (byte) i yourself inside the block.
You can take one more step. Use instanceof as a pattern, and it will cast the value for you.
if (i instanceof byte b) {
System.out.println(b);
}If i fits inside a byte, Java creates b as a byte and fills in the value. There is no manual cast and no range math. Your intent is clear at a single glance, which is exactly what good code should do.
| 💡 Interview Insight Interviewers love the gap between instanceof byte and instanceof byte b. The first form only tests whether the value fits the target type. The second form tests and then binds a fresh byte variable for you. Only the pattern form actually performs the cast. |
The humble switch also learns new tricks with JEP 532. It now accepts more primitive selectors and primitive case labels than before.
Here is a small helper that turns a rating into words. It switches directly on a float.
String floatToRating(float rating) {
return switch (rating) {
case 0f -> "0 stars";
case 2.5f -> "Average";
case 5f -> "Best";
case float f -> "Invalid rating: " + f;
};
}The first three cases match exact float values. The last case is a type pattern on float. It catches every other float value and still binds it to f. Because of that final case, the switch stays exhaustive without needing a default branch.
The compiler is strict about safety here, and that strictness protects you. This next switch compiles without any fuss.
void safeConversion(float f) {
switch (f) {
case double d -> System.out.println("As double: " + d);
}
}A float always fits inside a double. Therefore, the conversion never loses information. The compiler calls this an “unconditionally exact” conversion and is perfectly happy with it.
This next version, on the other hand, does not compile at all.
void unsafeConversion(float f) {
// error: the switch does not cover all possible values
switch (f) {
case int i -> System.out.println("As int: " + i);
}
}A float such as 100.01f cannot become an int without dropping the decimals. So this switch cannot cover every possible float value. Rather than letting a silent bug slip through, the compiler stops you with an error. In other words, the language now has your back.
There is one more quiet upgrade here. Earlier, a switch selector could only be char, byte, short, int, a String, or an enum. Now it can also be long, float, double, and boolean, along with their boxed types.
The boolean selector reads especially nicely. Take this small example.
boolean loggedIn = user.isLoggedIn();
String message = switch (loggedIn) {
case true -> "Welcome back!";
case false -> "Please sign in.";
};Both cases are covered, so this switch is exhaustive on its own. You do not need a default branch for a boolean. It is a tiny change, yet it makes simple two-way logic read very cleanly.
Records and pattern matching already work beautifully together. With JEP 532, primitive patterns can nest inside record patterns too. The same safety rules carry over without any extra effort.
Take a simple record with a single double component.
record Cup(double d) { }
// ...
Cup myCup = new Cup(34.567f);
if (myCup instanceof Cup(double d)) System.out.println("Cup contains double: " + d);
if (myCup instanceof Cup(float f)) System.out.println("Cup contains float: " + f);
if (myCup instanceof Cup(int i)) System.out.println("Cup contains int: " + i);The output looks like this:
Cup contains double: 34.56700134277344 Cup contains float: 34.567
Let us read the result together. The double and float checks pass, since the stored value fits both types. However, the int check fails, because its decimals would vanish during the conversion. So the very same safety logic you saw earlier flows straight into record patterns. That consistency is the whole point of the feature.
This matters more than it first looks. Records often model real data, such as a price, a coordinate, or a sensor reading. With primitive patterns, you can pick apart that data and check its shape in one clear line. Before, you had to extract each field and test it by hand. Now the pattern does both jobs together, and it stays safe while doing so.
Sometimes a plain type check is not enough. You also want to test the actual value. For that, JEP 532 lets you add a guard to a primitive pattern using a when clause.
int code = 250;
String label = switch (code) {
case int i when i < 100 -> "small";
case int i when i < 1000 -> "medium";
case int i -> "large";
};A guard is just a boolean test placed after the pattern. The case matches only when the pattern fits and the guard is also true. This keeps related checks together in one place. It also reads almost like plain English, which makes the logic easy to follow later.
Guards shine when you need bands or ranges. Think of score buckets, price tiers, or status codes. Without guards, you often end up with a messy ladder of if and else if. With guards, each range sits on its own line, right next to its result. The reader scans top to bottom and sees the whole story at once. For me, that readability is worth as much as the safety.
Enough theory for now. Let us actually run some of this code.
Remember that JEP 532 is a preview feature. So you must opt in on purpose. Java will never enable a preview feature for you by default.
First, make sure you are using a Java 27 JDK. You can check with java -version. After that, you pass the preview flags during both compilation and running.
javac --release 27 --enable-preview Demo.java java --enable-preview Demo
Two small points are worth remembering here:
--release 27 value must match your JDK version. A mismatch leads to a confusing error.Theory lands better with one file you can run. Here is a tiny Demo class that pulls a few ideas together. Save it, compile it with the flags above, and watch the output.
public class Demo {
record Reading(double value) { }
static String classify(int score) {
return switch (score) {
case int s when s < 0 -> "invalid";
case int s when s < 50 -> "low";
case int s when s < 80 -> "medium";
case int s -> "high";
};
}
public static void main(String[] args) {
int big = 2345323;
if (big instanceof byte b) {
System.out.println("Fits in byte: " + b);
} else {
System.out.println(big + " does not fit in a byte");
}
System.out.println("Score 72 is " + classify(72));
Reading r = new Reading(36.6);
if (r instanceof Reading(int whole)) {
System.out.println("Whole reading: " + whole);
} else {
System.out.println("Reading has decimals, not a whole int");
}
}
}Run it, and you will see each safety rule in action. The first block reports that a big int does not fit in a byte. Next, the guard in classify picks the right band for the score. Finally, the record check fails on the decimal reading, exactly as the rules promise.
Let me share a few lessons that will keep your code clean and save you some debugging time.
instanceof byte b over a hand-written range check. It is shorter and much harder to get wrong.switch refuses to compile, treat that as a warning about possible data loss.case float f to keep a switch exhaustive without a default.1000.010009765625.--enable-preview, which stops the code from compiling or running.float to int, to work inside a switch. It will not.1000.010009765625. That is normal binary rounding, not a defect in your code.instanceof byte and instanceof byte b. The first only tests, while the second also binds the value.| 💡 Interview Insight A likely follow-up is whether you should use preview features in production. The honest answer is no. Preview features can still change or even be removed. Use them to learn the feature and to send feedback, and wait for the final, permanent release before you ship real code. |
A: JEP 532 is the fifth preview of “Primitive Types in Patterns, instanceof, and switch” in Java 27. It lets primitive types like int, byte, float, and boolean appear in every pattern context, so instanceof, switch, and record patterns treat primitives the same way they treat reference types.
A: It is still a preview. The feature first appeared as JEP 455 in Java 23 and has been re-previewed without design change through Java 24, 25, 26, and now Java 27. So you must enable it on purpose, and you should not ship it to production until it becomes permanent.
A: Use a Java 27 JDK and pass the preview flags during both compile and run. For example: javac --release 27 --enable-preview Demo.java and then java --enable-preview Demo. The release value must match your JDK version.
A: You can write if (i instanceof byte b). The check passes only when the value fits the target type with no loss of information, and it also binds a new byte variable for you. This replaces manual range checks like i >= -128 && i <= 127 and removes a common source of silent overflow bugs.
A: Yes. Beyond char, byte, short, and int, a switch selector can now be long, float, double, and boolean, plus their boxed types. A boolean switch with case true and case false is exhaustive on its own, so it needs no default branch.
JEP 532 finishes a long and careful journey. It makes primitive types first-class citizens in pattern matching. Now instanceof, switch, and record patterns all treat primitives the same way they treat reference types.
Here is a quick recap of what we covered:
instanceof operator can test whether a cast is safe.switch blocks unsafe primitive conversions at compile time.Start small and build up. Try the instanceof byte b trick first, since it pays off right away. Then move on to switches and record patterns as you grow comfortable. Little by little, you will write primitive-handling code that is both shorter and safer.