22

I've been working with C# for a while and trying to get more familiar with Java. So I'm trying to migrate some of the basic patterns I use on daily basis in C# even only to understand the gap between JVM and dotnet and figure out how to deal with them. Here is the first problem I encountered - an option type - somethiong which is quite easy to achieve in many languages i.e. Koltlin:

sealed class Option<out T : Any> {
    object None : Option<Nothing>()
    data class Some<out T : Any>(val value: T) : Option<T>()}

so I can easily create a map functor:

fun <T : Any, B : Any> Option<T>.map(f: (T) -> B): Option<B> =
    when (this) {
        is Option.None -> Option.None
        is Option.Some -> Option.Some(f(this.value))} 

Is this something I can achieve in Java? Im not concerned about the lack of extentions methods, I can live without that, but how to perform the actual type matching without having to rely on an unchecked cast? At least thats what IntelliJ is complaining about...

Jon
  • 5,275
  • 5
  • 39
  • 51
Michal Pawluk
  • 561
  • 1
  • 5
  • 11
  • 1
    You are asking about Java, but show no Java code. I feel this isn't right. – M. Prokhorov Mar 26 '18 at 12:44
  • 2
    Neither C# nor Java have union types, and any workarounds will be awkward to use at best. For a .NET language that does have union types, please look at F#. – dumetrulo Mar 26 '18 at 13:00
  • 1
    Looks perhaps like you are looking for [java.util.Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) from Java 8. – OldCurmudgeon Mar 26 '18 at 13:06
  • 1
    There isn't anything exactly equivalent - but why do you need a Java solution, when you already have one in Kotlin, which has perfectly working interop? – Salem Mar 26 '18 at 13:15
  • @dumetrulo, you can easily simulate union types in C# without much typing, not as cleanly as in F# but perfectly usable, you can also do pattern-match generic types which is rather difficult in Java due to generic types erasure. The question how to achieve the same in Java. – Michal Pawluk Mar 26 '18 at 13:16
  • @Moira, you are right, unfortunately Java is the only approved JVM technology at my worklplace. – Michal Pawluk Mar 26 '18 at 13:24
  • 1
    @OldCurmudgeon, I seems like the java.util.Optional should do the job in case of the Option/Maybe type. Thanks for the suggestion. – Michal Pawluk Mar 26 '18 at 13:56
  • Thanks All, I guess I'll need to start my Java learning excersise with something more down-to-the-earth. – Michal Pawluk Mar 26 '18 at 13:58

2 Answers2

17

in the specific case you mentioned, the following would work:

Java doesn't have pattern matching. The closest you can get to pattern matching in Java is with the visitor pattern.

usage:

UnionType unionType = new TypeA();
    
Integer count = unionType.when(new UnionType.Cases<Integer>() {
    @Override
    public Integer is(TypeA typeA) {
        // TypeA-specific handling code
    }

    @Override
    public Integer is(TypeB typeB) {
        // TypeB-specific handling code
    }
});

boilerplate code:

interface UnionType {
    <R> R when(Cases<R> c);
    
    interface Cases<R> {
        R is(TypeA typeA);
    
        R is(TypeB typeB);
    }
}
    
class TypeA implements UnionType {

    // ... TypeA-specific code ...

    @Override
    public <R> R when(Cases<R> cases) {
        return cases.is(this);
    }
}
    
class TypeB implements UnionType {

    // ... TypeB-specific code ...

    @Override
    public <R> R when(Cases<R> cases) {
        return cases.is(this);
    }
}
Eric
  • 16,397
  • 8
  • 68
  • 76
  • Strikes me as hard to read and reason with, compared to some syntactic union type support. – dlamblin Nov 23 '21 at 00:24
  • @dlamblin , i agree. I wrote this up a while ago when Java didn't have native union type support. I'm not sure if Java has union type support not or not now though – Eric Nov 26 '21 at 20:53
  • I was trying to find out, it seems not as yet no. – dlamblin Nov 29 '21 at 20:46
  • 1
    There is a preview feature to get pattern matching in Java, based on switch expressions: https://openjdk.java.net/jeps/406 - seems quite comprehensive for the time being. – M. Prokhorov Jan 12 '22 at 14:02
0

When I bumped into this, my function needed to handle double[] or String[]. So I always passed both (setting the non-existing one to null) and the function was something like

public void myFunc(String[] valuesCategorical, double[] valuesNumeric){
    if (valuesCategorical == null){
        doSomethingWithCategorical(valuesCategorical);
    } else {
        doSomethingWithNumeric(valuesNumeric);
    }
}
Antoine
  • 862
  • 7
  • 22
  • 1
    Why not use overloads in such case? – solru Sep 18 '22 at 10:13
  • @solru Do you mean defining `doSomething(String[] values)` and `doSomething(double[] values)`? In my case, completely valid solution, but both do-something methods were different enough to deserve different names (IMO). However, if you mean defining `myFunc(String[] values)` and `myFunc(double[] values)`, that was undesirable, because I would end-up with "two copies" of the same function (my acual `myFunc` does more than just the if-else above) – Antoine Sep 19 '22 at 08:00
  • 1
    I fail to understand how this solves the problem. You will still need to know what you are working with beforehand... And in any case, this function is really bad design-wise since it aggregates multiple responsibilities of different actions into one method (which makes it harder to use, extend, and change in the future) – nir shahar Mar 03 '23 at 23:05