0

I would like to assert whether every element of an array is a sub type of a particular class. So I wrote a helper like this:

    private <T> void assertContainsAllSubtypes(
            List<? extends T> list,
            Class<? extends T>... expectedSubtypes) {
        Set<Class<? extends T>> subtypeSet = 
            new HashSet<>(Arrays.asList(expectedSubtypes));

        assertThat(list)
                .isNotEmpty()
                .hasSize(subtypeSet.size())
                .extracting(T::getClass)
                .containsExactlyInAnyOrderElementsOf(subtypeSet);   <-- ERROR
    }

But I get the following error:

Required type:

Iterable
<? extends Class<capture of ? extends T>>

Provided:

Set
<Class<? extends T>>

What am I missing?

Thanks.

7
  • i think it might be easier to skip the getClass() call and use hasOnlyElementsOfTypes() instead. i believe the problem with your current version is that using Object::getClass loses any type information from your generic parameters.
    – jtahlborn
    Commented Jul 2 at 20:57
  • Doesn't help: ``` Required type: Iterable <? extends capture of ? extends T> Provided: Set <Class<? extends T>> ```
    – bdhar
    Commented Jul 2 at 21:30
  • .hasSize(subtypeSet.size()) looks suspicious. I don't think it's related to the error, but because subtypeSet is a Set, it does mean that your assertion can be satisfied only by Lists whose elements are all of different classes from each other. Commented Jul 2 at 21:57
  • 1
    Additionally, I think the hasSize() assertion is redundant with the containsExactlyInAnyOrderElementsOf() assertion, which is stronger. And the isNotEmpty() seems redundant too, except inasmuch as isNotEmpty() makes it impossible for an empty list to satisfy the assertion even in the event that zero expectedSubtypes are provided. Commented Jul 2 at 22:05
  • 1
    Also, why T::getClass instead of Object::getClass? You don't need to preserve the T here, because in practice, any Class object is acceptable. Indeed, I don't follow why the method needs to be generic at all, but surely any point to that is satisfied by type checking at the point of invocation. Commented Jul 2 at 22:18

1 Answer 1

0

I believe this will compile:

  private <T, U extends T> void assertContainsAllSubtypes(
      List<T> list,
      Class<U>... expectedSubtypes) {
    Set<Class<U>> subtypeSet =
        new HashSet<>(Arrays.asList(expectedSubtypes));

    assertThat(list)
        .isNotEmpty()
        .hasSize(subtypeSet.size())
        .extracting(T::getClass)
        .containsExactlyInAnyOrderElementsOf(subtypeSet);
  }

Although i still think it's simpler to just do this:

  private <T, U extends T> void assertContainsAllSubtypes(
      List<T> list,
      Class<U>... expectedSubtypes) {

    assertThat(list)
        .isNotEmpty()
        .hasOnlyElementsOfTypes(expectedSubtypes);
  }

(TIL that you can do T::getClass for a generic type)

Not the answer you're looking for? Browse other questions tagged or ask your own question.