Wszystko pochodzi od sprzętu.
Bajt to najmniejsza adresowalna jednostka pamięci na większości urządzeń.
Każdy wspomniany typ jest zbudowany z pewnej wielokrotności bajtów.
Bajt ma 8 bitów. Dzięki temu możesz wyrazić 8 booleanów, ale nie możesz wyszukać tylko jednego na raz. Adresujesz 1, adresujesz wszystkie 8.
Kiedyś było to takie proste, ale potem przeszliśmy z 8-bitowej magistrali na 16, 32, a teraz 64-bitową.
Co oznacza, że chociaż wciąż możemy adresować na poziomie bajtów, nie możemy odzyskać ani jednego bajtu z pamięci bez uzyskania sąsiednich bajtów.
W obliczu tego sprzętu projektanci języków postanowili pozwolić nam wybrać typy, które pozwoliły nam wybrać typy pasujące do sprzętu.
Możesz twierdzić, że taki szczegół może i powinien zostać wyodrębniony, zwłaszcza w języku, który ma działać na dowolnym sprzęcie. Miałoby to ukryte problemy z wydajnością, ale możesz mieć rację. Po prostu tak się nie stało.
Java faktycznie próbuje to zrobić. Bajty są automatycznie promowane do Ints. Fakt, który doprowadzi Cię do szału przy pierwszej próbie wykonania jakiejkolwiek poważnej zmiany.
Dlaczego więc nie zadziałało dobrze?
Wielką zaletą Javy jest to, że można było usiąść ze znanym dobrym algorytmem C, wpisać go w Javie i przy drobnych poprawkach zadziałałoby. A C jest bardzo blisko sprzętu.
Utrzymywanie tego i wyodrębnianie rozmiaru z integralnych typów po prostu nie działało razem.
Więc mogliby. Po prostu nie.
Być może programista nie chciałby, aby ktoś mógł użyć liczby większej niż określony rozmiar, co pozwala mu to ograniczyć.
To jest prawidłowe myślenie. Istnieją na to sposoby. Funkcja zacisku dla jednego. Język może posunąć się tak daleko, że ustanowi dowolne granice dla swoich typów. A kiedy te granice są znane w czasie kompilacji, pozwoli to na optymalizację sposobu przechowywania tych liczb.
Java po prostu nie jest tym językiem.