0

Java 17

I'm implementing the following method:

public static <T, O extends T, V> Optional<V> toOption(T t, Function<? super O, ? extends Optional<V>> f, Class<O> cls){
    Optional<O> opt;
    if(cls.isAssignableFrom(t.getClass())){
        opt = some((O) t);
    } else
        opt = empty();
    return opt.flatMap(f);
}

which is supposed to return non empty Optional if the runtime type of argument of type T is subclass of type parameter O and flatMap it with the given Function. The problem with this implementation is that the following still compiles:

toOption(123, v -> Optional.of(v), String.class);

But Integer and String are unrelated. So O extends T does not really works. Is there a way to make such cases not compile?

Some Name
  • 8,555
  • 5
  • 27
  • 77
  • 2
    You're doing it. The inference engine goes: Oh, great, if `T` is `Object` everything works great - `123` (`Integer`) is an `Object`, and `String` indeed extends `Object`. What you 'want' doesn't make sense. Separate from the non-sensical notion (which is that you want to treat, presumably, java as if it is invariant, it isn't), this method is a bad idea. `Class` to attempt to reify generics is a bad idea (you can't then have e.g. `List` for O, you can't stick generics in there. Just an example. – rzwitserloot Apr 14 '23 at 04:01
  • If `t` must be an instance of a subclass, make the superclass `abstract` and all parameters will be subclasses because there won't be any instances of the parent classes. – Bohemian Apr 14 '23 at 04:18
  • What does this have to do with Functional Programming? – Jared Smith Apr 14 '23 at 15:50
  • 1
    @JaredSmith I needed something like a Prism that's a functional programming concept – Some Name Apr 15 '23 at 00:33

1 Answers1

1

To simplify the question, I believe you cannot enforce a complication error for a function like this, if the arguments do not match the generic rule:

public <A, B extends A> void doNothing(A a, Class<B> b) {
}

@Test
void test() {   
    doNothing(123, String.class);
}

However, you can simplify the code a bit and you can throw runtime exceptions if the types do not match. Though, since you are going with Optionals, you can simply stick to Optional.empty, as you had in your example. A simple refactor:

public static <T, O extends T, V> Optional<V> toOption(
        T t, Function<? super O, ? extends Optional<V>> f, Class<O> cls) {

    return assign(t, cls)
            .flatMap(x -> some(x))
            .flatMap(f);
}

private static <T, O> Optional<O> assign(T t, Class<O> cls) {
    if (cls.isAssignableFrom(t.getClass())) {
        return Optional.ofNullable((O) t);
    } 
    return Optional.empty();
}
Emanuel Trandafir
  • 1,526
  • 1
  • 5
  • 13