private TypeConstraint combine(TypeBound boundI,TypeBound boundJ){
switch (boundI.variance) {
case INV:
switch (boundJ.variance) {
case INV:
      return combineInvInv(boundI,boundJ);
case CO:
case CONTRA:
    return combineInvVar(boundI,boundJ);
}
break;
case CO:
switch (boundJ.variance) {
case INV:
return combineInvVar(boundJ,boundI);
case CONTRA:
return combineContraCo(boundJ,boundI);
case CO:
return combineBothCoOrBothContra(boundI,boundJ);
}
break;
case CONTRA:
switch (boundJ.variance) {
case INV:
return combineInvVar(boundJ,boundI);
case CO:
return combineContraCo(boundI,boundJ);
case CONTRA:
return combineBothCoOrBothContra(boundI,boundJ);
}
}
throw new IllegalStateException("unreachable");
}

private TypeConstraint combine ( TypeBound boundI , TypeBound boundJ ) { switch ( boundI . variance ) { case INV : switch ( boundJ . variance ) { case INV : return combineInvInv ( boundI , boundJ ) ; case CO : case CONTRA : return combineInvVar ( boundI , boundJ ) ; } break ; case CO : switch ( boundJ . variance ) { case INV : return combineInvVar ( boundJ , boundI ) ; case CONTRA : return combineContraCo ( boundJ , boundI ) ; case CO : return combineBothCoOrBothContra ( boundI , boundJ ) ; } break ; case CONTRA : switch ( boundJ . variance ) { case INV : return combineInvVar ( boundJ , boundI ) ; case CO : return combineContraCo ( boundI , boundJ ) ; case CONTRA : return combineBothCoOrBothContra ( boundI , boundJ ) ; } } throw new IllegalStateException ( "unreachable" ) ; }
in terms of jls8 , this method embodies the implication rules listed in sec . 18 . 3 . 1 ( the other implication rules in jls8 take as input capture conversion constraints ) .
