4

I have a Predicate<Object> and need an equivalent Predicate<Animal>.

Predicate<Animal> provideIsSentientPredicate() {
    // Won't compile -- cannot convert from Predicate<Object> to Predicate<Animal>
    return Predicates.instanceOf(Human.class);
}

Predicates are contravariant, so converting a Predicate<Object> to a Predicate<Animal> is safe. Is there clean and readable way to convert from a Predicate<Object> to a Predicate<Animal> (e.g. without suppressing warnings)?

I'd prefer not to change my method's type signature to return a Predicate<? super Animal> or Predicate<Object> unless someone convinces me that is the correct thing to do.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Robert Cooper
  • 1,270
  • 9
  • 11
  • Related question for Optional: http://stackoverflow.com/questions/7848789/how-to-use-guava-optional-as-naturally-covariant-object – Robert Cooper Apr 22 '13 at 17:20
  • The type of `Predicates.instanceOf` is absolutely intended to push you to either return a `Predicate super Animal>`, or to return `Predicate` and change the caller to expect a `Predicate super Animal>`. – Louis Wasserman Apr 22 '13 at 17:31
  • @Louis Wow! Really? That wasn't my expectation at all! I've always thought it was rude to make a client deal with a wildcard type and so I've never written a method with a wildcard return type. Are wildcard return types ever considered good practice? I can't recall any method in the Guava source tree that returns a wildcard type -- is there one? – Robert Cooper Apr 22 '13 at 17:57
  • I don't have a serious preference one way or the other, but if you dislike returning wildcard types, then it's totally legit to return `Predicate` and force the client to _consume_ it as a `Predicate super Animal>`. Types are weird. – Louis Wasserman Apr 22 '13 at 18:06
  • 1
    Okay, I've been convinced that the examples from `Predicates` as how they want you to use it: return specific types, even if they're more general than necessary, and _consume_ `Predicate super Foo>`. – Louis Wasserman Apr 22 '13 at 18:18
  • @Louis So, in this case, return the specific type `Predicate`? – Robert Cooper Apr 22 '13 at 18:20
  • No, return `Predicate`, and change the calling code to treat it as a `Predicate super Animal>`. – Louis Wasserman Apr 22 '13 at 18:21
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/28682/discussion-between-robert-cooper-and-louis-wasserman) – Robert Cooper Apr 22 '13 at 18:21

1 Answers1

4
Predicate<Animal> provideIsSentientPredicate() 
{
    return cast( Predicates.instanceOf(Human.class) );
}

static <A, B extends A> Predicate<B> cast(Predicate<A> pa)
{
    @SuppressWarnings("unchecked")
    Predicate<B> pb = (Predicate)(pa);
    return pb;

    // we know it works correctly in practice.
    // or if you are a theorist, create a wrapper predicate
    //
    //     Predicate<B>
    //         boolean apply(B b)
    //             return pa.apply(b);
}

BTW, there's no reason why Guava shouldn't declare the method as

static <T> Predicate<T> instanceOf(Class<?> clazz)

consistent with other sibling methods.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
ZhongYu
  • 19,446
  • 5
  • 33
  • 61
  • 1
    Yeah, I couldn't figure out why it isn't `static Predicate instanceOf(Class> clazz)` either, but the Guava team usually has good (if subtle) reasons for this sort of thing. Guava avoids generics in other static factory methods (`Ordering.allEqual()`, `Ordering.arbitrary()`, `Ordering.usingToString()`), and @Louis comment seems to suggest that `Predicates.instanceOf()`'s return type is intentional. Still no clue why, though. – Robert Cooper Apr 22 '13 at 19:54
  • 2
    In reply to my previous comment, I would guess the most likely reasons the method signature of `Predicate instanceOf(Class> clazz)` isn't `static Predicate instanceOf(Class> clazz)` is that second signature is more complicated, is harder to test, is harder to read, is less forward compatible, and doesn't hold advantages over the first signature in enough cases to make the switch. BUT, if there's a deeper reason than that, I would really be curious to hear it. – Robert Cooper Apr 22 '13 at 20:07