code: protected List<TypeVariableName> getTypeVariables(TypeName t) {
      return match(t)
          .when(typeOf(TypeVariableName.class)).get(
              v -> {
                if (v.bounds.isEmpty()) {
                  return ImmutableList.of(v);
                } else {
                  return Stream.concat(
                      Stream.of(v), v.bounds.stream()
                          .map(b -> getTypeVariables(b))
                          .flatMap(b -> b.stream()))
                      .collect(Collectors.toList());
                }
              })
          .when(typeOf(ParameterizedTypeName.class)).get(
              p -> p.typeArguments.stream()
                  .map(v -> getTypeVariables(v))
                  .flatMap(l -> l.stream())
                  .collect(Collectors.toList()))
          .orElse(Collections.emptyList())
          .getMatch();
    }
docs: Returns a list of type variables for a given type.
  
  <p>There are several cases for what the returned type variables will be:
  <ul>
  <li>If {@code t} is a type variable itself, and has no bounds, then a singleton list
  containing only {@code t} will be returned.</li>
  <li>If {@code t} is a type variable with bounds, then the returned list will first contain
  {@code t} followed by the type variables for the bounds</li>
  <li>If {@code t} is a parameterized type, then the returned list will contain the type
  variables of the parameterized type arguments</li>
  <li>Otherwise an empty list is returned</li>
  </ul>