Tråd: JAVA Data types i Java.
View Single Post
Limited edition
Moff's Avatar
Er litt sen til festen her, men jeg tenkte bare å uttdype litt (i tilfelle noen finner tråden senere og har flere spørsmål, for eksempel).

De fleste sier at oppgaven er teit, men den har faktisk et veldig klart og utvetydig svar. Årsaken er at vi snakker om Java, og der er det en fast mekanikk for hvordan datatyper fungerer.

Fasiten er altså:

0 - int
"hello" - String
101 - int
-1 - int
true - boolean
"33" - String
3.1415 - double

En av de første tingene jeg lurte på da jeg startet med Java var hvorfor noen datatyper uttrykkes med liten forbokstav (int, char, byte, boolean, double etc) og andre ikke (String). For å gjøre det enda mer forvirrende; hvorfor kan man også uttrykke noen datatyper på to ulike måter (int = Integer, double = Double etc)?

Svaret er allerede nevnt, og det handler om primitive vs ikke-primitive datatyper. Alle typer som uttrykkes med liten forbokstav i Java er primitive. Den viktigste egenskapen til en primitiv datatype er at den ikke kan være null. Alle ikke-primitive typer kan være null (noe som leder deg til den vanligste exception-en i programmering, NullPointerException a.k.a. NullReferenceException). En null-tilstand er det som skjer når en variabel er definert, men ikke har fått noen verdi. Litt som dette:

String minString;

minString er nå en String, men den har ingen verdi. Den er derfor null.

Med primitive typer er dette anderledes. Om du definerer en int slik:

int minInt;

... så vil den ikke være null, den vil ha verdien 0. Det samme gjelder double, float, long og byte. Booleans vil være false som standard.

Om du prøver å skrive dette:

int minInt = null;

... så vil du få en exception (error).

Neste punkt: Hvorfor kan du uttrykke int som Integer, double som Double og byte som Byte?
Når en primitiv type skrives med stor forbokstav, så betyr det at du bruker en såkalt wrapper. En wrapper i dette tilfellet er en klasse (class) som wrapper en primitiv type. Funksjonelt så er det ikke så mye forskjell på dem, men i noen tilfeller så vil ikke Java la deg bruke primitive datatyper. Et typisk eksempel på dette er når du skal lage lister og maps. Et eksempel:

Map<Integer, Boolean> mittMap = new HashMap<Integer, Boolean>();

Her lager jeg et HashMap som har int som key og boolean som value. Her må du bruke wrapperne; du kan ikke lage et Map med primitive typer. Dette handler om hvordan et Map fungerer internt; du må ha tilgang til noen metoder på de datatypene du bruker, for å kunne generere hash-er og slike ting. Prøv å skrive noe slik:

int minInt = 3;
String test = minInt.toString();

Dette vil ikke fungere, fordi en primitiv type (int) ikke har noen properties.
Prøv dette i stedet:

Integer minInt = 3;
String test = minInt.toString();

Dette fungerer fordi wrapperen Integer tilbyr alle properties du trenger.

Bruk alltid primitive typer der du kan (liten forbokstav). Wrappere trenger du (nesten) bare når du oppretter lister og maps.

Siste punkt: Hvorfor er fasiten på oppgaven akkurat sånn? Hvorfor er 0 en int i stedet for en byte?
Dette handler utelukkende om hvordan Java tenker. Når du lager variabler, så er det en standard for hvilken datatype det blir. Det er ingen generell regel for hvordan Java gjør dette. Den velger ikke den billigste (minste) typen; det ville jo være byte for 0. Den velger heller ikke den mest nøyaktige typen, det ville være long for 0. I stedet ser det ut til at den velger den mest brukte typen, som er int. Da får vi noe som er midt i mellom.

3 av standard-typene er enkel å forklare på denne måten. int er midt i mellom billig og nøyaktig. int er 32-bit signed, noe som betyr at den kan lagre tall fra minus 2147483648 til pluss 2147483647. Java bruker bare signed-typer uansett; noen andre programmeringsspråk vil la deg velge unsiged også (uint). En unsigned int vil ikke kunne lagre negative verdier, men kan imidlertid lagre dobbelt så høye tall (opp til 4294967295).

Booleans er de eneste typene som kan ha verdiene false og true. Alle andre typer kan evaluere til true eller false, men det er kun boolean som kan være true og false. En int med verdi 0 vil for eksempel evaluere til false, mens int = 1 blir true. Dette gjør at du kan skrive if-statements som if(minInt) { og dermed sjekke om tallet er 0 eller ikke. Men dette er i alle fall grunnen til at false og true alltid er en boolean.

String er også one-of-a-kind i Java; når du omslutter noe med "anførselstegn", så er det String du får. Som det er blitt nevnt, så er String også egentlig en wrapper, i den forstand at den wrapper en liste med char. Char (character) er en primitiv type som representerer én bokstav, da som en tallverdi. Du kan derfor caste en char som både String og int, og se både en bokstav og et tall.

Den aller siste typen i oppgaven er en liten luring. Den bryter med konseptet "midt mellom billig og nøyaktig". En double er 64-bit, i motsetning til int. Det er float som er 32-bit, så om du skulle sammenligne disse 4 typene, så kunne du sagt at int og float hører sammen (32-bit), mens long og double hører sammen (64-bit).

Så: hvorfor velger Java som standard double når du skriver tall med desimaler?
Dette har med nøyaktighet å gjøre. Et tall med desimaler, det vi kaller floating point numbers i programmering, skaper problemer med nøyaktighet. En int er en int uansett hvordan du vrir og vender på den. Et helt tall kan du caste frem og tilbake mellom int og long uten at du vil miste nøyaktighet. Det samme er ikke tilfelle når vi snakker om floating point numbers. Det er i all hovedsak to ulike typer floating point numbers i Java; single precision og double precision.

Du kan allerede nå gjette hvorfor typene heter "float" og "double". Float får navnet sitt fordi det er et floating point number. Double får navnet sitt fordi det også er et floating point number, men det har double precision. Float er single precision.

Kort fortalt, en double vil være vesentlig mer nøyaktig når det kommer til avrunding, for eksempel. En double tar mer plass, og tradisjonelt sett er den tregere enn en float - men moderne prosessorer er faktisk av og til raskere på å regne ut double precision-numbers enn single precision. Det handler bare om at gode prosessorer er bedre optimalisert for avanserte utregninger, og derfor er det ikke lengre noe stort tap for performance å bruke double i stedet for float.

Dette er årsaken til at Java foretrekker double. Jeg skal ikke si "bruk double i stedet for float der du kan"; men du bør være bevisst på de valgene du tar. Jeg foretrekker double selv, men float har sine bruksområder den også.

Bonus: Hvordan deklarerer du et tall eksplisitt som float hvis double er standard? Hva med long?
Dette er litt sært, men du kan gjøre det ved å skrive en bokstav bak tallet. D for double, F for float og L for long. Slik:

public minMetode(float tall) {
// Utregninger
}

// Kall metoden med en float:
minMetode(200f);
minMetode(20.0f);

// Andre eksempler:
// 200d - double
// 200l - long

Her skriver jeg "f" for å fortelle Java at jeg vil ha en float. Hvis jeg ikke hadde gjort det, så ville 200 blitt tolket som int og 20.0 tolket som double. Om du ikke eksplisitt sier at du skal ha float i slike situasjoner, så vil du på ett eller annet tidspunkt få et uforventet resultat som følge av at du mister nøyaktighet når du caster fra double til float (og lignende).

Integer har ingen slik funksjon så vidt jeg vet, men det er rett og slett fordi det aldri vil være nødvendig å deklarere en eksplisitt 32-bit int.