xipengyang
xipengyang

Reputation: 96

Why is it <T> Matcher<T> as return type in Hamcrest instanceOf method

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

Answers (2)

rolve
rolve

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

mystarrocks
mystarrocks

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 Matchers 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

Related Questions