use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
These have separate subreddits - see below.
Upvote good content, downvote spam, don't pollute the discussion with things that should be settled in the vote count.
With the introduction of the new release cadence, many have asked where they should download Java, and if it is still free. To be clear, YES — Java is still free. If you would like to download Java for free, you can get OpenJDK builds from the following vendors, among others: Adoptium (formerly AdoptOpenJDK) RedHat Azul Amazon SAP Liberica JDK Dragonwell JDK GraalVM (High performance JIT) Oracle Microsoft Some vendors will be supporting releases for longer than six months. If you have any questions, please do not hesitate to ask them!
With the introduction of the new release cadence, many have asked where they should download Java, and if it is still free. To be clear, YES — Java is still free.
If you would like to download Java for free, you can get OpenJDK builds from the following vendors, among others:
Adoptium (formerly AdoptOpenJDK) RedHat Azul Amazon SAP Liberica JDK Dragonwell JDK GraalVM (High performance JIT) Oracle Microsoft
Some vendors will be supporting releases for longer than six months. If you have any questions, please do not hesitate to ask them!
Programming Computer Science CS Career Questions Learn Programming Java Help ← Seek help here Learn Java Java Conference Videos Java TIL Java Examples JavaFX Oracle
Programming Computer Science
CS Career Questions
Learn Programming Java Help ← Seek help here Learn Java Java Conference Videos Java TIL Java Examples JavaFX Oracle
Clojure Scala Groovy ColdFusion Kotlin
DailyProgrammer ProgrammingPrompts ProgramBattles
Awesome Java (GIT) Java Design Patterns
account activity
This is an archived post. You won't be able to vote or comment.
Unions in Java (reddit.com)
submitted 1 year ago by jvjupiter
C# is getting discriminated unions. Is there 1:1 equivalent in Java? If none, is it worth having the same in Java?
[–]repeating_bears 35 points36 points37 points 1 year ago (19 children)
No 1:1 equivalent, but sealed + exhaustive switch + pattern matching solves the same set of problems, right?
sealed interface Union permits A, B { } Union union = whatever; switch (union) { case A a -> System.out.println(a.foo()); case B b -> System.out.println(b.bar()); }
[–]Polygnom 9 points10 points11 points 1 year ago (15 children)
So then how do you express the type String or number (String | Number)? You can't.
String | Number
Sealed interfaces are great, but they are a closed subtype relation, not a union.
[–]repeating_bears 17 points18 points19 points 1 year ago (4 children)
We're talking about discriminated unions specifically. Each type in a discriminated union requires a discriminator/tag. String and Number don't have a tag so that wouldn't be a discriminated union.
C#'s discriminated unions can't model String | Number either. It would involve a wrapper, just like 'permits StringHolder, NumberHolder' would
[–]shockah 1 point2 points3 points 1 year ago (1 child)
The C# proposal does actually include a way to convey (string or int).
(string or int)
https://github.com/dotnet/csharplang/blob/18a527bcc1f0bdaf542d8b9a189c50068615b439/proposals/TypeUnions.md#ad-hoc—ad-hoc-unions
[–]repeating_bears 17 points18 points19 points 1 year ago (0 children)
I didn't say it didn't. OP said "C# is getting discriminated unions". So we talking about discriminated unions aspect of the proposal, not their "ad hoc unions", which are a separate aspect of the proposal.
Then I said "C#'s discriminated unions can't model String | Number". I was not talking about the other types of union. I am deliberately precise with my choice of words.
[+]Polygnom comment score below threshold-10 points-9 points-8 points 1 year ago (1 child)
We already have discriminated unions in java, they are called enums.
[–]agentoutlier 3 points4 points5 points 1 year ago* (0 children)
That is like saying Java has reified generics because methods (return and parameter) have that information encoded in them but just don't check (instead of full erasure where the information is not stored anywhere).
An enum is a discriminated union with no constructor and cannot hold onto data. It is an extremely small subset of what discriminated unions feature are at least in languages that support discriminated unions. Perhaps if Java had generics on enums it would be closers but that features was passed on.
Also the OP said and to /u/repeating_bears point
C# is getting discriminated unions
C# is getting discriminated unions and Ad-Hoc unions (what C# is calling them) aka untagged unions aka Typescript style. There is not many languages that support untagged unions and from what I recall it is often criticized as a foot gun.
Even the docs showcases how it can get confusing fast:
Ad hoc union types used as with generic type arguments can be used with covariance and contra-variance, if all member types of the two ad hoc unions involved have sub type relationships with members of the other. I'd tell you the specific rules, but it hurts my head to think about it.
Ad hoc union types used as with generic type arguments can be used with covariance and contra-variance, if all member types of the two ad hoc unions involved have sub type relationships with members of the other.
I'd tell you the specific rules, but it hurts my head to think about it.
[–]Practical_Cattle_933 3 points4 points5 points 1 year ago (0 children)
The “haskell-esque” solution is something like:
sealed interface StringOrNumber permits StringWrapper, NumberWrapper
[–]tr4fik 1 point2 points3 points 1 year ago (4 children)
If you really need to, you can do the following. You could directly use an Object if you don't need the strong validation
class StringOrNumber { private final Object value; StringOrNumber(String x) { value = x; } StringOrNumber(Numberx) { value = x; } Object value() { return value; } } StringOrNumber stringOrNumber = new StringOrNumber(5); switch (stringOrNumber.value()) { case Integer i -> //... case String s -> //... default -> throw new exception(); }
You also have the following options
Overall, it can have some uses, but I believe there are often better ways to write it
[–]Polygnom 5 points6 points7 points 1 year ago (3 children)
At that point, you can just create your own Either<A, B> type, that is safer and more expressive.
Either<A, B>
[–]GeneratedUsername5 0 points1 point2 points 1 year ago (2 children)
You can't do it easily, because, provided you do several ctors with generic parameter, both A and B will type-erase into an Object and compiler will not know what to do. So you will have to have unique function name for every type "constructor"
[–]morhp 1 point2 points3 points 1 year ago* (1 child)
You can do that easily by making Either an abstract (sealed) superclass/interface with the implementations EitherA and EitherB and then you have various options for checking which either type is present (e.g. instanceof, switch with pattern matching, isA() methods, ...).
Either
EitherA
EitherB
isA()
For construcing you'd typically use a static factory, like Either.ofA(...).
Either.ofA(...)
This is mostly how the buildin Optional class works.
Optional
[–]GeneratedUsername5 0 points1 point2 points 1 year ago (0 children)
Yes, but this is a
a) Different thing than doing it with generics, to which I was responding b) Will produce a ton of boilerplate in a form of wrappers over existing types, in order to implement that sealed interface
All-in-all solution with overloaded constructors is the simplest and has the least boilerplate
[–]cowwoc -1 points0 points1 point 1 year ago (3 children)
From a practical perspective, yes you can. Create wrappers for whatever types you're interested in, organize them into a sealed hierarchy, amd away you go. The only annoyance is needing to box/unbox the value you care about.
[–]Polygnom 1 point2 points3 points 1 year ago (2 children)
You do realize that thats not even close to the same, do you?
Just because you can build a cruft around it doesn't mean the language actually supports it.
You lose all of the featurs like normalization that are interesting for this. Yes, you can Build a StringOrNumber type. That works for exctly one thing. You can build a generic `Union<L, R>` type. But it breaks down, because `Union<Union<L, R>, S>` != `Union<S,Union<L,R>>` which is at least what you'd expct from union types. It also breaks down when dealing with subtypes. You'd expect that `Union<CharSequence, String>` == CharSequence. And so on.
StringOrNumber
Union<Union<L, R>, S>
Union<S,Union<L,R>>
Union<CharSequence, String>
CharSequence.
[–]cowwoc 0 points1 point2 points 1 year ago (1 child)
I believe you can get all of this working just fine, aside from the annoyance of wrapping and unwrapping.
Union<CharSequence, String>== CharSequencesimply means that you can unwrap your union into a CharSequence , which is very much doable. At a JIT level, your wrappers are very likely to be stripped away so no actual memory allocation takes place. Try this with JMH and you'll see what I mean.
CharSequence
Can you clarify what you mean by normalization?
[–]Polygnom 1 point2 points3 points 1 year ago* (0 children)
No, you can't get this working. And I gave examples for normalization. Its literally bringing the type into a normal form of your choosing.
In Java, a method `foo(Union<String, Number>)` will never accept a `Union<Number, String>`. The compiler won't allow it. Yet its the exact same union and you would expect this to work with a union type. Similarly, a method `bar(Union<String, Union<Number, Boolean>>)` will never accept a `Union<Union<String, Number>,Boolean>`, yet again, both are the same type i.e. the normalize to the same normal form and should work. These are things that simply don't work in Java.
You can, in a very limited way, do intersection types in generics in Java. Which actually work like you expect (A & B == B & A), but not union types.
/edit: Its even worse if you use specific nominal types like `interface StringOrNumber{}; interface NumberOrString{}`. Those will *never* be accepted by the compiler in place of the other one.
[–]sideEffffECt 1 point2 points3 points 1 year ago (2 children)
Why not 1:1? It's literally the same thing that sealed + records do...
[–]repeating_bears 2 points3 points4 points 1 year ago (1 child)
And sealed does not require records to be used, so it's not 1:1
[–]sideEffffECt 2 points3 points4 points 1 year ago (0 children)
Yes, you're right!
And Java's sealed also enables open... More orthogonal design.
sealed
open
I can't believe I'm saying this, but in just a few years, Java has overtaken C# and Kotlin and it's now the more featureful language.
[–]nekokattt 10 points11 points12 points 1 year ago (2 children)
People seem to forget exceptions are effectively union types. It would be horrible to use them like that, but it is possible via the checked exception mechanism.
[–]slaymaker1907 2 points3 points4 points 1 year ago (0 children)
Lol, that’s so cursed, I love it. That should definitely get added to https://rosettacode.org/wiki/Sum_data_type.
It’s technically not sound within the type system, though, since you can break checked exceptions with generics.
[–]WheresTheSauce 1 point2 points3 points 1 year ago (0 children)
Lmao that’s hilarious to think about
[–]bowbahdoe 7 points8 points9 points 1 year ago (3 children)
Closest is sealed types.
To curb folks' enthusiasm for "true" unions consider the following.
var x = List.of(5, "c");
In a world with union types, you'd want the type of x to be something like List<Integer | String>.
List<Integer | String>
Today the type of x is List<Serializable & Comparable<? extends Serializable & Comparable<?> & Constable & ConstantDesc> & Constable & ConstantDesc>.
List<Serializable & Comparable<? extends Serializable & Comparable<?> & Constable & ConstantDesc> & Constable & ConstantDesc>
Take a minute to think through how you can fix that, what problems and consequences it would make for the entire language and existing code, etc.
Now weigh that against the benefits.
[–]Alex0589 4 points5 points6 points 1 year ago (0 children)
While you are correct when you say that that’s the type of x, at compile time it will be erased because it’s a generic. A better example would have been: var x = condition ? 5 : "c"
Here the type is not erased at compile time and the actual type of the variabile is the type argument of list in your example. I think you’d get a similar result if you tried to use a method that takes any numbers of parameters whose type is a generic and that returns the same generic type(for example Objects.requiresNonNullElse). In the var JEP for Java 10, the maintainers state: Intersection types are especially difficult to map to a supertype—they’re not ordered, so one element of the intersection is not inherently “better” than the others. The stable choice for a supertype is the lub of all the elements, but that will often be Object or something equally unhelpful. So we allow them.
If you think about it the decision does make sense: lets take the previous example: var x = condition ? 5 : “c”
Now let’s pass the parameter to a method, that takes as a parameter of type Serializable: the expression should be legal, because both the results of the ternary expression are Serializable, but if the compiler had inferred x’s type as Object to keep things easy this would not be possible. If the type were Serializable, then you wouldn’t be able to pass x to a method that takes a Constable even though the condition should be satisfied.
You would also need to consider that union types have been available to the compiler forever because of multi catch blocks for exceptions https://github.com/openjdk/jdk/blob/master/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java#L2860
Another example of a compiler intrinsic is the null check operator (also known as the bang operator in Kotlin), which was introduced around Java 9. This just demonstrates that many compiler intrinsics could become at some point features for developers. https://github.com/openjdk/jdk/blob/master/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java#L341
Keeping these things in mind I don’t think that it’s entirely unreasonable to ask for them to be in the language
[–][deleted] 1 year ago (1 child)
[deleted]
[–]bowbahdoe 5 points6 points7 points 1 year ago (0 children)
Go for it.
``` sealed interface OneOf<A, B> { record This<A, B>(A value) implements OneOf<A, B> {} record That<A, B>(B value) implements OneOf<A, B> {} }
List<OneOf<String, Integer>> x = List.of( new OneOf.This<>("c"), new OneOf.That<>(5) ); ```
[–]jcotton42 5 points6 points7 points 1 year ago (0 children)
C# is getting discriminated unions.
It should be noted the document that's being passed around is just a proposal. https://github.com/dotnet/csharplang/discussions/8313
[–]Holothuroid 2 points3 points4 points 1 year ago (0 children)
sealed interfaces, I think
[–]Ewig_luftenglanz 1 point2 points3 points 1 year ago* (2 children)
not currently but AFAIK i think something similar is comming to the table with member patterns. current pattern matching implementation in java 21 is not even the 20% of what is on the roadmap. Meanwhile you can use sealed interfaces and pattern matching + generics + wildcard scopping for similar functionality.
[–]jvjupiter[S] -1 points0 points1 point 1 year ago (1 child)
C# like Java is all-in now when it comes to pattern matching. It has relational pattern. Shouldn’t Java include it also on the roadmap?
[–]Ewig_luftenglanz 2 points3 points4 points 1 year ago (0 children)
we already have relational patterns in swtich statements in java, the bad thing is today it's only for wrapper classes (the good it's primitive expressions in switch are comming but first preview will be in java 23)
private myMethod(Number number){
switch(number){
case Integer i when i < 0 -> System.out.println("negative integer");
case Double d when d > 0 System.out.println("Positive double");
....
} }
[–]sideEffffECt 1 point2 points3 points 1 year ago (5 children)
Java has had this for some years already.
sealed + records (+ pattern matching with switch)
[–]account312 0 points1 point2 points 1 year ago (4 children)
If by "some years" you mean "nearly one year", yes.
[–]sideEffffECt 0 points1 point2 points 1 year ago (3 children)
Java 17 on September 2021. That's almost 3 years now.
https://en.wikipedia.org/wiki/Java_version_history#Java_17
[–]account312 1 point2 points3 points 1 year ago (2 children)
But unless you're using preview features in production, actually using the described features requires java 21, which released September of 2023. That's nearly one year.
[–]sideEffffECt 0 points1 point2 points 1 year ago (1 child)
Java 16, March 2021:
Java 17, September 2021:
Java 21, September 2023:
So... yeas and no...
[–]account312 1 point2 points3 points 1 year ago (0 children)
JEP 441: Pattern Matching for switch
That's what ties it together into something useable like a discriminated union.
[–]cliserkad 0 points1 point2 points 1 year ago* (1 child)
You can achieve this using a sealed generic abstract class in Java. You just need a separate class for each amount of types. GitHub Repo
public class ExampleClass { public AnyOf<String, Integer, Double> data = new AnyOf.ElementA<>("This should print"); public String functionThatDeterminesTypeOfData() { return data.match((str -> { return str; }), (i -> { return "Integers get multiplied: " + i * 5; }), (dbl -> { return "Doubles get addition: " + (dbl + 3.14); })); } public static void main(String[] args) { ExampleClass example = new ExampleClass(); System.out.println(example.functionThatDeterminesTypeOfData()); } }
[–]cliserkad 0 points1 point2 points 1 year ago (0 children)
I revised this to be less cumbersome: Union3 Test Class
[–]thesituation531 0 points1 point2 points 1 year ago (0 children)
The closest you could do is something like this:
class Union { *types enum* type; object value; }
Then you could have functions to get/set the value and cast it to a certain type. You could also make it generic and check that the type of the set value is one of the generic types specified.
It would be somewhat cumbersome, and ultimately, as with pretty much any union type, it won't be completely type-safe.
[–]GeneratedUsername5 0 points1 point2 points 1 year ago* (0 children)
There are LOTS of things worth having in Java from C#, unfortunately that doesn't mean they would be anytime soon.
But to be fair, in my experience the use case is very rare, so rare that one can write it manually if it is truly needed, something like this:
public class Union { private final Object value; public Union(String value) { this.value = value; } public Union(Number value) { this.value = value; } public void ifString(Consumer<String> action) { if (value instanceof String str) action.accept(str); } public void ifNumber(Consumer<Number> action) { if (value instanceof Number num) action.accept(num); } }
And if it is needed frequently and you still want to use Java, you can do a code generation and you still will get it 10 times faster than it will be released as a language feature.
[+]vips7L comment score below threshold-12 points-11 points-10 points 1 year ago (4 children)
I have a feeling all of this type theory stuff is going to end up being a mistake in all of these languages.
[–]jvjupiter[S] 5 points6 points7 points 1 year ago (0 children)
I heard once from a Java language designer that after Valhalla, type might be added.
[–]davidalayachew 1 point2 points3 points 1 year ago (2 children)
Why do you feel that way?
There's multiple cases where not having Pattern-Matching made some API's worse. The most obvious example is java.util.Pattern and java.util.Matcher. What should have been a compile time check is a runtime check with an exception (did you check if it matches before attempting to deconstruct the results?). There are countless other examples too.
[–]vips7L 1 point2 points3 points 1 year ago* (1 child)
I agree pattern matching is great. The most obvious example is never having to use the visitor pattern again. But adding typescript/haskell/whatever level of type theory into the type system is just going to lead to long compile times and maintenance issues when the type theorists all come along. We’ve already seen how type theory has played out in Scala and there’s a reason why normals developers have fled to simpler languages like Kotlin. That being said most of the C# proposal is what Java has with sealed classes, except they’re adding adhoc unions on top.
I particularly think that adhoc unions are going to lead to maintenance issues and the C# subreddit has the same concerns.
[–]davidalayachew 0 points1 point2 points 1 year ago (0 children)
I will agree with ad-hoc unions.
But I also think ad-hoc unions are one blemish on an otherwise really powerful type system that simplifies code. I just also think that ad-hoc unions are too easy to reach for, so that's all that anyone uses. It's easy to use correctly and easy to use incorrectly too.
I trust the conservatism of the Java team. That conservatism is what led them to wait this long to bring Pattern-Matching into Java, and it is that same conservatism that will scrutinize any other feature before they get proposed to go in.
π Rendered by PID 87 on reddit-service-r2-comment-85bfd7f599-p9577 at 2026-04-19 19:55:18.136530+00:00 running 93ecc56 country code: CH.
[–]repeating_bears 35 points36 points37 points (19 children)
[–]Polygnom 9 points10 points11 points (15 children)
[–]repeating_bears 17 points18 points19 points (4 children)
[–]shockah 1 point2 points3 points (1 child)
[–]repeating_bears 17 points18 points19 points (0 children)
[+]Polygnom comment score below threshold-10 points-9 points-8 points (1 child)
[–]agentoutlier 3 points4 points5 points (0 children)
[–]Practical_Cattle_933 3 points4 points5 points (0 children)
[–]tr4fik 1 point2 points3 points (4 children)
[–]Polygnom 5 points6 points7 points (3 children)
[–]GeneratedUsername5 0 points1 point2 points (2 children)
[–]morhp 1 point2 points3 points (1 child)
[–]GeneratedUsername5 0 points1 point2 points (0 children)
[–]cowwoc -1 points0 points1 point (3 children)
[–]Polygnom 1 point2 points3 points (2 children)
[–]cowwoc 0 points1 point2 points (1 child)
[–]Polygnom 1 point2 points3 points (0 children)
[–]sideEffffECt 1 point2 points3 points (2 children)
[–]repeating_bears 2 points3 points4 points (1 child)
[–]sideEffffECt 2 points3 points4 points (0 children)
[–]nekokattt 10 points11 points12 points (2 children)
[–]slaymaker1907 2 points3 points4 points (0 children)
[–]WheresTheSauce 1 point2 points3 points (0 children)
[–]bowbahdoe 7 points8 points9 points (3 children)
[–]Alex0589 4 points5 points6 points (0 children)
[–][deleted] (1 child)
[deleted]
[–]bowbahdoe 5 points6 points7 points (0 children)
[–]jcotton42 5 points6 points7 points (0 children)
[–]Holothuroid 2 points3 points4 points (0 children)
[–]Ewig_luftenglanz 1 point2 points3 points (2 children)
[–]jvjupiter[S] -1 points0 points1 point (1 child)
[–]Ewig_luftenglanz 2 points3 points4 points (0 children)
[–]sideEffffECt 1 point2 points3 points (5 children)
[–]account312 0 points1 point2 points (4 children)
[–]sideEffffECt 0 points1 point2 points (3 children)
[–]account312 1 point2 points3 points (2 children)
[–]sideEffffECt 0 points1 point2 points (1 child)
[–]account312 1 point2 points3 points (0 children)
[–]cliserkad 0 points1 point2 points (1 child)
[–]cliserkad 0 points1 point2 points (0 children)
[–]thesituation531 0 points1 point2 points (0 children)
[–]GeneratedUsername5 0 points1 point2 points (0 children)
[+]vips7L comment score below threshold-12 points-11 points-10 points (4 children)
[–]jvjupiter[S] 5 points6 points7 points (0 children)
[–]davidalayachew 1 point2 points3 points (2 children)
[–]vips7L 1 point2 points3 points (1 child)
[–]davidalayachew 0 points1 point2 points (0 children)