Eu comecei a fazer antes do staroski postar e resolvi terminar. Experimentei algumas ideias na solução, então usei essa solução como um exercício para essas ideias. Mas você pode ver se lhe é útil:
import java.util.Arrays;
import java.util.List;
public class Intervalo<T extends Comparable<T>> {
private final T min;
private final T max;
public Intervalo(T min, T max) {
validarIntervalo(min, max);
this.min = min;
this.max = max;
}
public T getMin() {
return min;
}
public T getMax() {
return max;
}
public List<Intervalo<T>> mesclarCom(Intervalo<T> outroIntervalo) {
return mesclarIntervalos(this, outroIntervalo);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((max == null) ? 0 : max.hashCode());
result = prime * result + ((min == null) ? 0 : min.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Intervalo<?> other = (Intervalo<?>) obj;
if (max == null) {
if (other.max != null)
return false;
} else if (!max.equals(other.max))
return false;
if (min == null) {
if (other.min != null)
return false;
} else if (!min.equals(other.min))
return false;
return true;
}
@Override
public String toString() {
return "["+getMin()+", "+getMax()+"]";
}
public static <T extends Comparable<T>> List<Intervalo<T>> mesclarIntervalos(Intervalo<T> intervalo1, Intervalo<T> intervalo2) {
if (intervalo1.getMax().compareTo(intervalo2.getMin()) < 0) return Arrays.asList(intervalo1, intervalo2);
if (intervalo2.getMax().compareTo(intervalo1.getMin()) < 0) return Arrays.asList(intervalo2, intervalo1);
int minCompareResult = intervalo1.getMin().compareTo(intervalo2.getMin());
int maxCompareResult = intervalo1.getMax().compareTo(intervalo2.getMax());
T newMin = (minCompareResult > 0) ? intervalo2.getMin() : intervalo1.getMin();
T newMax = (maxCompareResult > 0) ? intervalo1.getMax() : intervalo2.getMax();
return Arrays.asList(new Intervalo<T>(newMin, newMax));
}
public static <T extends Comparable<T>> void validarIntervalo(T min, T max) {
int compareResult = min.compareTo(max);
if (compareResult >= 0) throw new IllegalArgumentException("Min '"+min+"' não pode ser Maior ou Igual a Max '"+max+"'!");
}
Classe de teste do JUnit5:
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
class Test_Intervalo {
@Test
final void testValidarIntervalo() {
new Intervalo<>(5, 8);
assertThrows(IllegalArgumentException.class, () -> new Intervalo<>(8, 5));
assertThrows(IllegalArgumentException.class, () -> new Intervalo<>(8, 8));
}
@Nested
@DisplayName("MesclarIntervalos")
class MesclarIntervalos {
@Test
@DisplayName("[2, 5]+[4, 9] --> [2, 9]")
final void testMesclarIntervalos1() {
Intervalo<Integer> intervaloA = new Intervalo<>(2, 5);
Intervalo<Integer> intervaloB = new Intervalo<>(4, 9);
Intervalo<Integer> intervaloR = new Intervalo<>(2, 9);
assertEquals(intervaloR, intervaloA.mesclarCom(intervaloB).get(0));
assertEquals(intervaloR, intervaloB.mesclarCom(intervaloA).get(0));
}
@Test
@DisplayName("[3, 5]+[1, 8] --> [1, 8]")
final void testMesclarIntervalos2() {
Intervalo<Integer> intervaloA = new Intervalo<>(3, 5);
Intervalo<Integer> intervaloB = new Intervalo<>(1, 8);
Intervalo<Integer> intervaloR = new Intervalo<>(1, 8);
assertEquals(intervaloR, intervaloA.mesclarCom(intervaloB).get(0));
assertEquals(intervaloR, intervaloB.mesclarCom(intervaloA).get(0));
}
@Test
@DisplayName("[5, 8]+[1, 3] --> [1, 3][5, 8]")
final void testMesclarIntervalos3() {
Intervalo<Integer> intervaloA = new Intervalo<>(5, 8);
Intervalo<Integer> intervaloB = new Intervalo<>(1, 3);
assertEquals(intervaloB, intervaloB.mesclarCom(intervaloA).get(0));
assertEquals(intervaloA, intervaloB.mesclarCom(intervaloA).get(1));
assertEquals(intervaloB, intervaloA.mesclarCom(intervaloB).get(0));
assertEquals(intervaloA, intervaloA.mesclarCom(intervaloB).get(1));
}
}
}
Veja que eu testei os exemplos que você deu, você pode adicionar mais testes para ver se esse código realmente funcionará em qualquer situação.
O resultado dos Testes no Eclipse IDE (vi que você disse que não está usando IDE, mas eu usei):

Nota: eu acho que ficaria muito legal se fosse uma interface fluente para as mesclagens, algo assim:
intervalos = new Intervalo(1, 3).add(new Intervalo(5,8)).add(new Intervalo(3, 10));
Mas não consegui encontrar rapidamente uma forma de fazer assim, talvez você consiga, se gostar dessa ideia 
“intervalos
” seria de algum Tipo que oferece um Intervalo<T>
ou uma lista de Intervalo<T>
, talvez dê para tirar ideias da interface desse Tipo da classe Optional.