Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics Hell - How do I pass a joda.DateTime to Hamcrest Matcher.greaterThan?

JodaTime has

public final class DateTime extends BaseDateTime {...}

which works its way up to

public interface ReadableInstant extends Comparable<ReadableInstant>

Hamcrest has

public static <T extends java.lang.Comparable<T>> org.hamcrest.Matcher<? super T>
    greaterThan(T value) {...}

If I try

greaterThan(new DateTime());

then I get a compile error (Eclipse gives most clue)

The generic method greaterThan(T) of type Matchers is not applicable for the arguments (DateTime). The inferred type DateTime is not a valid substitute for the bounded parameter >

Am I right in thinking that the signature of greaterThan should actually be

public static <T extends java.lang.Comparable<? super T>> org.hamcrest.Matcher<? super T>     
    greaterThan(T value)

? And is there a way to fit these together short of casting to the raw Comparable?

like image 562
Duncan McGregor Avatar asked Nov 01 '11 17:11

Duncan McGregor


2 Answers

Yes, it looks to me like that would be a better signature.

Have you tried specifying the comparison type explicitly?

Matchers.<ReadableInstant>greaterThan(new DateTime());

I don't believe you can call it using a static import and also specifying the type argument, unfortunately - but that may not be too much of hardship.

Of course an alternative is to cast the argument:

greaterThan((ReadableInstant) new DateTime());

I don't have Hamcrest handy, but the above worked fine for me using the signature you'd given me, in a test type.

like image 161
Jon Skeet Avatar answered Oct 05 '22 08:10

Jon Skeet


If you use it often and are bothered by the conversion, you can also implement your own Matcher like this:

public static Matcher<AbstractPartial> isAfter(final AbstractPartial partial) {
    return new BaseMatcher<AbstractPartial>(){

        @Override
        public void describeTo(final Description description) {
            description.appendText("after partial: ").appendValue(partial);
        }

        @Override
        public boolean matches(final Object object) {
            if (object instanceof AbstractPartial) {
                return ((LocalDate) object).isAfter(partial);
            }
            return false;
        }
    };
}

And test it like this:

    Set<LocalDate> dates = Sets.newHashSet(new LocalDate(2013, 1, 1), new LocalDate(2013, 1, 2), new LocalDate(
        2013, 1, 3));
    assertThat(
        CollectionUtils.isEqualCollection(filter(isAfter(new LocalDate(2013, 1, 1)), dates),
            Lists.newArrayList(new LocalDate(2013, 1, 2), new LocalDate(2013, 1, 3))), is(true));

If you want to use DateTime instead of LocalDate, just substitute AbstractPartial with AbstractInstant in the first listing.

like image 22
Gismo Ranas Avatar answered Oct 05 '22 09:10

Gismo Ranas