Przyczyna jest taka, że Java w czasie wykonania zaciera (erasure) informację o konkretnym typie użytym jako parametr. Wszystkie typy konkretna stają się jakby typem Object. Dla Javy nie będzie znaczenia czy użyjesz new TabSpa<Integer>[7] czy new TabSpa<String>[7] ponieważ jak mógłbyś się przekonać wyrażenie takie jak to:
TabSpa<Integer>[7].getClass() == TabSpa<String>[7].getClass()
da w wyniku... True.
Zabawne prawda?
Po prostu inaczej niż w C++ nigdy nie możesz dowiedzieć się nic o typie konkretyzującym. W żaden sposób.
Użycie TabSpa<?> jest nie tyle bardziej bezpieczne, ile pokazuje, że jest niebezpieczne i w ten sposób przestajesz bazować na tym czym może być "?". W rzeczywistości TabSpa<?>() nie różni się niczym od TabSpa(). Ale dzięki temu musisz być po prostu ostrożny i starać się sprawdzać typy przed użyciem ich zamiast zakładać, że żaden inny typ Ci się do kontenera nie wpakuje (bo może).
Z tego samego powodu nie można też zrobić czegoś takiego:
class TabSpa<T> extends T
{
...
}
nad czym czasem boleję. ;-)