Reputation: 96
I am having trouble understanding the method signature of the 'instanceOf' method in hamcrest package. Here is the method
public static <T> Matcher<T> instanceOf(Class<?> type) {
return (Matcher<T>) new IsInstanceOf(type);
}
I can understand the return type is Matcher<T>
and first <T>
declares generic. But T
is never inferred in the method. Notice we don't pass T
as argument type.
One question arises: does it mean T
is unbounded and we can dynamically cast return Matcher to any type?
Upvotes: 4
Views: 1445
Reputation: 10218
I think the answer to this question is simple. Since the type parameter T
is not used in any of the parameters of the instanceOf
method, the compiler is allowed to infer any type to use as a type argument for that method call. Therefore, the compiler can always infer a type that "works" in the context of the method call (simply speaking).
This all just means that a line like
assertThat(something, instanceOf(Some.class));
will always type check, no matter the static type of something
, because the compiler will infer T
to be the static type of something
, both for the assertThat
and for the instanceOf
call.
And this is a good thing, because the whole point of the line is to check the dynamic type of something
, which is not statically known. If the precise type of something
were statically known, this assertion would not even be necessary.
Upvotes: 0
Reputation: 4088
The Matcher
type that instanceOf
returns is not really useful until you actually... well, use it.
And yes, you can assign the resulting Matcher
to any type.
For example, you can do this:
// <T> is inferred to be 'Object' here
Matcher<Object> objectMatcher = instanceOf(Integer.class);
or this
// <T> is inferred to be 'Integer' here
Matcher<Integer> integerMatcher = instanceOf(Integer.class);
and try to use the Matcher
s with assertThat
in these ways:
// succeeds and expectedly so
// Integer.class.isInstance(5) is true
assertThat(5, objectMatcher);
// succeeds and expectedly so
// Integer.class.isInstance(5) is true
assertThat(5, integerMatcher);
// fails, but only after asserting, by throwing an AssertionError at runtime
// Integer.class.isInstance(Double) is false;
// but we wouldn't have gotten this far had we used the right matcher
assertThat(5.5d, objectMatcher);
// fails at compile time; exactly what we want generics to offer us - compile time safety
assertThat(5.5d, integerMatcher);
// you can also do it this way
// if you wanted to force T instead of
// expecting the compiler to infer it for you based on the context
assertThat(5, IsInstanceOf.<Integer>instanceOf(Integer.class));
Compare these invocations with the signature of assertThat
to understand what T
(a different T
from the one used with instanceOf
!) and ? super T
would be in each case:
public static <T> void assertThat(T actual, Matcher<? super T> matcher)
// actual would be Integer and matcher would be Matcher<Object>
assertThat(5, objectMatcher);
// actual would be Integer and matcher would be Matcher<Integer>
assertThat(5, integerMatcher);
// actual would be Double and matcher would be Matcher<Object>
assertThat(5.5d, objectMatcher);
// actual would be Double and matcher would be Matcher<Integer>; see the mismatch?
assertThat(5.5d, integerMatcher);
Upvotes: 1