jocarreira / hamcrest

Automatically exported from code.google.com/p/hamcrest
0 stars 0 forks source link

Allow better typing for instanceOf matchers #10

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
assertThat is

<T> assertThat(T value, Matcher<T> matcher)

However, instanceOf is

public static <T> Matcher<T> instanceOf(Class<T> instanceOf)

This means that the expression

Throwable e = ...;
assertThat(e, instanceOf(AssertionError.class));

is not type-safe, according to at least the Eclipse compiler.

I'll be honest--I've spent some serious time on this one, and haven't found
a great solution.  For now, the best I've come up with is the evil:

    @SuppressWarnings("unchecked") @Test public void testSomething() {
        assertThat(e, is((Class<Throwable>)(Class<?>)AssertionError.class));
    }

I'm hoping one of you has a better idea.  Thanks!

Original issue reported on code.google.com by david.s...@gmail.com on 6 Feb 2007 at 6:15

GoogleCodeExporter commented 8 years ago
I think the best solution is to simply relax the typing:

public static Matcher<Object> instanceOf(Class<?> instanceOf)

This makes more sense with Java's typing rules, anyhow.

Original comment by david.s...@gmail.com on 9 May 2007 at 3:46

GoogleCodeExporter commented 8 years ago
I agree with relaxing the rules. If we could always rely on the compiler, there 
would
be no point. I'll look into how we can make this work.

Original comment by joe.wal...@gmail.com on 21 May 2007 at 10:13

GoogleCodeExporter commented 8 years ago
I had a go at changing the signature of isInstanceOf() from:
  <T> Matcher<T> isInstanceOf(Class<T> type)
to:
  Matcher<Object> isInstanceOf(Class type)

This worked, but unfortunately breaks backwards compatibility (and I can see 
some
instances where the current approach is desirable).

I think it would be better to add a new method for this and leave the existing 
one in
place. But I'm stumped for names - any suggestions?

Original comment by joe.wal...@gmail.com on 22 May 2007 at 3:44

GoogleCodeExporter commented 8 years ago
What are the current points of breakage/compatibility?  I can imagine
with(instanceOf(String.class)) as a way to get type safety in method 
expectations. 
In that case, the ideal would be to have

<T> Matcher<T> anyInstanceOf(Class<T> type) for the old method, and
Matcher<Object> instanceOf(Class type) for the new one.

That said, breaking backwards compatibility is bad, so how about castableTo for 
the
new name?

Also, does this mean it would not be possible to have is(Class) point to the new
meaning?  I like the "is" alias, and would be sad to lose it.

Original comment by david.s...@gmail.com on 22 May 2007 at 3:53

GoogleCodeExporter commented 8 years ago
conformsTo would be better than isCastableTo

Original comment by smgfree...@gmail.com on 22 May 2007 at 7:23

GoogleCodeExporter commented 8 years ago
Okay, scratch my last comments. The only place it broke backwards compatibility 
was
inside is(). 

I've changed both is(Class) and isInstanceOf(Class) to return Matcher<Object>. 
Now
checked in.

Original comment by joe.wal...@gmail.com on 22 May 2007 at 8:41

GoogleCodeExporter commented 8 years ago
I think the problem here is the definition of assertThat, the code defined in 
the
first post compiles if we define assertThat as:

public static <T, S extends T> void assertThat(T actual, Matcher<S> matcher)

Having said that, I haven't given a thorough analysis to the implications of 
defining
assertThat like this...

Original comment by alexg...@gmail.com on 5 Jul 2008 at 2:44

GoogleCodeExporter commented 8 years ago
That type signature makes no sense.  This would:

 public static <T, S super T> void assertThat(T actual, Matcher<S> matcher)
                     ^^^^^

If it was S extends T, then any matcher can be passed to assertThat, because T 
can be
bound to Object and everything extends Object.  It would be equivalent to
assertThat(T actual, Matcher<?> matcher).

We want assertThat to only allow matchers that are statically type compatible.

For example, assuming the subtyping relationships Object < X < Y and a value, 
y, of
type Y.  I should obviously be able to pass y and a Matcher<Y> to assertThat.  I
should be able to pass y and a Matcher<X> to assertThat, because y is an X by
inheritance.  And I should be able to pass y and a Matcher<Object> to 
assertThat, for
the same reason.

But, if Z1 and Z2 both extend Y, I should not be able to pass y and a 
Matcher<Z1> or
Matcher<Z2> to assertThat because I don't, at compile time, know whether that is
type-safe or not.

If I

Original comment by nat.pr...@gmail.com on 5 Jul 2008 at 2:55