Skip to content

Instantly share code, notes, and snippets.

@danieldietrich
Last active May 21, 2019 11:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save danieldietrich/67fd2d86fcdc0a184392 to your computer and use it in GitHub Desktop.
Save danieldietrich/67fd2d86fcdc0a184392 to your computer and use it in GitHub Desktop.
Simple but interesting Java generics example.

Interestingly I had to use

static <T> Predicate<T> instanceOf(Class<? extends T> type)

instead of

static <T> Predicate<? super T> instanceOf(Class<T> type)

Those two are roughly the same, but I had to use the former in order to make the following compile:

Example 1: Pattern matching with the partial function Case

Try.of(() -> "ok")
   .recover(Case(instanceOf(Error.class), "fixed"))
   .get();

Here are the relevant type signatures which are involved:

Try<T> recover(Function<? super Throwable, ? extends T> f) { ... }

static <T, R> Case<T, R> Case(Predicate<? super T> predicate, R retVal) { ... }

interface Case<T, R> extends PartialFunction<T, R> {
}

interface PartialFunction<T, R> extends Function<T, R> {
    boolean isApplicable(T t);
}

Example 2: Standalone predicate vs usage in Case

Throwable x = new Error("error");

// COMPILE ERROR: test(Error) in Predicate cannot be applied to (Throwable)
//                           v
instanceOf(Error.class).test(x);

// COMPILE ERROR: test(capture<? super Error>) in Predicate cannot be applied to (Throwable)
//                                                        v
((Predicate<? super Error>) instanceOf(Error.class)).test(x);

// -- Given: class Match<T> { <R> R of(Case<? super T, ? extends R>... cases) }

// COMPILES FINE!
Match(x).of(
    Case(instanceOf(Error.class), "ok"),
    Case(instanceOf(CompilerError.class), "huh!?")
);

Match.Case<Error, String> _case1 = Case(instanceOf(Error.class), "ok");
Match.Case<? super Throwable, ? extends String> _case2 = Case(instanceOf(Error.class), "ok");

// COMPILE ERROR: Cannot resolve method 'of(Case<Error, String>)'
Match(x).of(
    _case1
);

// COMPILES FILE!
Match(x).of(
    _case2
);

This solution fixes the compile errors of Example 2 (see above).

Interestingly no unchecked casts are needed here. However, in Example 2 the compiler is not able to correctly infer the types.

Update: Narrowing only works in this special case, when checking for instanceof. In general this is no viable solution and may break semantics.

Update 2: As Lukas remarked, the narrow method is not needed. See comments and examples below.

Throwable x = new Error("error");

Predicate<Throwable> p1 = narrow(instanceOf(Error.class));
p1.test(x);

Predicate<? super Throwable> p2 = narrow(instanceOf(Error.class));
p2.test(x);

with

static <T extends U, U> U narrow(T t) { return t; }
@lukaseder
Copy link

I don't see why you need this, or why it should be called narrow

@danieldietrich
Copy link
Author

@lukaseder Oh, you are right. Think I had looked too long on this screen while fiddling around with generics. Works all fine without magic...

public class Test {

    public static void main(String[] args) {

        Number i = 1;
        Number b = new BigDecimal("1");

        { // Test 1
            Predicate<? super Number> isInt = n -> n instanceof Integer;
            isInt.test(i); // true
            isInt.test(b); // false
        }

        { // Test 2
            Predicate<? super Number> isInt = instanceOf(Integer.class);
            isInt.test(i); // true
            isInt.test(b); // false
        }

    }

    static <T> Predicate<T> instanceOf(Class<? extends T> type) {
        Objects.requireNonNull(type, "type is null");
        return obj -> obj != null && type.isAssignableFrom(obj.getClass());
    }
}

Note: So the oddities mentioned above can be healed by first creating a Predicate of the right type and then testing an object:

Throwable x = new Error("error");
Predicate<? super Throwable> p = instanceOf(Error.class);
p.test(x); // = true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment