Python versus Java


Hello World

For å i det hele tatt ha et fungerende program i Java, trengs det en del mer kode enn i Python. Hvor Python enkelt nok begynner å lese kommandoer fra toppen av filen, trenger Java at koden som skal kjøres er definert i en klasse; og at denne klassen har en metode med den magiske signaturen public static void main(String[]). Det er denne metoden som blir kalt når programmet starter.

# hello_world.py
print("Hello Python")



 
// HelloWorld.java
public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello Java");
  }
}

I eksemplene som sammenligner Python og Java videre på denne siden vil vi av plasshensyn ikke alltid inkludere hele dette oppsettet, men det må likevel alltid være med for at koden skal kjøre; og det vil være tilstede dersom du klikker på «se steg» -knappen.

I løpet av dette kurset skal du lære om betydningen av alle de magiske ordene og tegnene vi kan se i Java-programmet over. Noe dukker opp på dag én, andre ting overser vi relativt lenge og kommer tilbake til senere i kurset.

Kompilering og å kjøre programmet fra terminalen

Å kjøre Hello World -programmet vi skrev over fra terminalen gjøres med én kommando i Python, mens Java krever to: først skal kildekoden kompileres med javac -programmet, deretter kan programmet kjøres med «java runtime environment» (java -programmet).

$ python hello_world.py
Hello Python
$
 
$ javac HelloWorld.java
$ java HelloWorld
Hello Java
$

Når javac -programmet kjøres, vil det bli opprettet nye filer med filendelse .class. I eksempelet over vil det opprettes en fil HelloWorld.class. Denne filen inneholder kode i et programmeringsspråk som kalles «java bytecode». Denne kompilerte koden er en slags mellomting mellom menneskeleselig kode og maskinleselig kode. Dersom du av en eller annen grunn åpner den i en tekst-editor vil du se en blanding av forståelige og uforståelige symboler.

Dersom man kjører et Java-program ved å trykke på «kjør» -knappen i en kodeeditor som Visual Studio Code eller Intellij, vil begge stegene bli gjort automatisk. Forskjellen vi merker som utviklere er at selve kompileringsprosessen (javac -steget) kan krasje – og dermed vil ikke Java -programmet engang begynne å kjøre. I Python ville programmet kanskje ikke krasjet før kjøringen faktisk kom til linjen hvor noe var feil.

Fordelen med å kompilere koden før den kjøres er først og fremst effektivitet. Under kompileringsprosessen kan kompilatoren identifisere deler av koden som kan skrives på en mer effektiv måte, og dessuten er det raskere å oversette bytecode til maskinkode enn det er å oversette menneskelig-leselig kode direkte. I følge denne testen er ren Java ca 30 ganger raskere enn ren Python (men nøyaktig hvor stor forskjellen er vil variere mye avhengig av hvilken oppgave som skal utføres, og også av hvilken fortolker/kjøretidsmiljø man benytter seg av).

Illustrasjon av veien fra kildekode til program som kjører på maskinen for Python og Java.

Variabler og statiske typer

Til forskjell fra Python, har variabler en type i Java. Vi sier at Java er et statisk typet språk (i motsetning til Python som er et dynamisk typet språk).

# Python
x = 42
x += 1
print(x)
// Java
int x = 42;
x += 1;
System.out.println(x);

Første gang en variabel benyttes, må man oppgi typen. Typen kan ikke endres, og skal heller ikke oppgis ved senere bruk.

Noen enkle typer

De vanligste typene er omtrent de samme i Python som i Java:

# Pythons vanligste typer:
# int, str, float, bool
x = 42
s = "Dette er en streng"
f = 3.14
b = True
print(x, s, f, b)
// Javas vanligste typer:
// int, String, double, boolean
int x = 42;
String s = "Dette er en streng";
double f = 3.14;
boolean b = true;
System.out.println(x + " " + s + " " + f + " " + b);

De oppfører seg nesten likt også. Det er imidlertid noen subtile forskjeller i int -typen: mens en int i Python kan ha ubegrenset stor verdi, kan en int i Java kun eksistere i intervallet [-2147483648, 2147483647]. For å få samme type int som Python, kan man med Java bruke BigInteger i stedet; men det gjør vi av effektivitetshensyn kun når det faktisk er nødvendig (altså nesten aldri).

Typekonvertering

Typekonvertering mellom de primitive typene, som for eksempel int, double og boolean, kan gjøres ved såkalt casting. Dette gjøres ved å sette den nye typen i parenteser foran verdien som skal konverteres:

# Python
i = int(3.9) # 3
f = float(3) # 3.0
b = bool(1) # True
// Java
int i = ((int) 3.9); // 3
double f = ((double) 3); // 3.0
boolean b = ((boolean) 1); // true

Konverteringen til og fra strenger er litt annerledes, og krever at vi bruker funksjoner laget spesielt for formålet:

# Python
si = str(42) # "42"
sf = str(3.14) # "3.14"

i = int("42") # 42
f = float("3.14") # 3.14
// Java
String si = Integer.toString(42); // "42"
String sf = Double.toString(3.14); // "3.14"

int i = Integer.parseInt("42"); // 42
double f = Double.parseDouble("3.14"); // 3.14

Operasjoner

Presedensreglene for operatorer er omtrent de samme i Python og Java. Se offisiell presdenstabell.

Operatorsymboler er også stort sett de samme i Python og Java, men med unntak for noen logiske operatorer:

# Python
x = True
y = False

a = x and y
b = x or y
c = not x

s = "A" if x else "B"
// Java
boolean x = true;
boolean y = false;

boolean a = x && y;
boolean b = x || y;
boolean c = !x;

String s = x ? "A" : "B";

Divisjon

Divisjon-operatøren / oppfører seg litt annerledes enn i Python. I Java utfører denne operatøren heltallsdivison når den brukes med int-verdier, mens den i Python alltid utfører flyttallsdivisjon. Operatøren // finnes ikke i Java (dette brukes i stedet for å angi kommentarer i kildekoden).

# Python
# Heltalsdivisjon
x = 5 // 2
print(x) # 2

# Flyttallsdivisjon
y = 5 / 2
print(y) # 2.5
// Java
// Heltallsdivisjon
int x = 5 / 2;
System.out.println(x); // 2

// Flyttallsdivisjon: omgjør til double først
double y = ((double) 5) / 2;
System.out.println(y); // 2.5

I Java er det mulig å bruke ++ og -- som operatører på variabler av typen int. Dette gjør at variabelen øker eller minker med 1. Dersom ++ eller -- skrives før variabelen, endres verdien i variabelen før resten av linjen utføres, mens dersom de skrives etter variabelen, endres verdien etter at resten av linjen er utført.

Det er også mulig å bruke += og -=, som gjør at variabelen øker eller minker med et gitt tall slik som i Python.

# Python
x = 10
x += 2 # Øker x med 2
x += 1 # Øker x med 1

# Skriver ut x og øker så med 1
print(x) # 13
x += 1
# Skriver ut x igjen
print(x) # 14

# Øker x med 1, skriver så ut
x += 1
print(x) # 15
// Java
int x = 10;
x += 2; // Øker x med 2
x++; // Øker x med 1

// Skriver ut x og øker så med 1
System.out.println(x++); // 13

// Skriver ut x igjen
System.out.println(x); // 14

// Øker x med 1, skriver så ut
System.out.println(++x); // 15
 

Lister

Lister er en av de mest brukte datatypene i Python, og det samme gjelder i Java. I Java finnes det sågar flere ulike liste-varianter; den som ligner mest på en liste slik vi kjenner det fra Python er ArrayList. Men det er likevel noen forskjeller:

# Python
# Opprette en tom liste
a = []

# Legge til elementer
a.append(10)
a.append(11)

# Konvertere listen til streng
sa = str(a)
print(sa) # [10, 11]

# Størrelsen på listen
print(len(a)) # 2

# Hente ut plass 1
print(a[1]) # 11

# Endre plass 1
a[1] = 42 

# Les siste element
print(a[-1])

# Sjekk om 42 er i listen
print(42 in a)

# Opprette ikke-tom liste
b = ["ha", "he", "hi", "ho"]



# Fjerne element på plass 1
s2 = b.pop(1)
print(s2) # he

# Fjern "xx" hvis den er i listen
# r blir True hvis noe ble fjernet
r = False
if "xx" in b:
    b.remove("xx")
    r = True
print(r) # False

# Finn posisjonen til "x" eller
# gi -1 hvis "x" ikke er i listen
i = b.index("x") if "x" in b else -1
print(i) # -1
// Java
// Opprette en tom liste
ArrayList<Integer> a = new ArrayList<>();

// Legge til elementer
a.add(10);
a.add(11);

// Konvertere listen til streng
String sa = a.toString();
System.out.println(sa); // [10, 11]

// Størrelsen på listen
System.out.println(a.size()); // 2

// Hente ut plass 1
System.out.println(a.get(1)); // 11

// Endre plass 1
a.set(1, 42);

// Les siste element
System.out.println(a.get(a.size() - 1));

// Sjekk om 42 er i listen
System.out.println(a.contains(42));

// Opprette ikke-tom liste
ArrayList<String> b = new ArrayList<>(
    Arrays.asList("ha", "he", "hi", "ho")
);

// Fjerne element på plass 1
String s1 = b.remove(1);
System.out.println(s1); // he

// Fjern "xx" hvis den er i listen
// r blir true hvis noe ble fjernet
boolean r = b.remove("xx");
System.out.println(r); // false




// Finn posisjonen til "x" eller
// gi -1 hvis "x" ikke er i listen
int i = b.indexOf("x");
System.out.println(i); // -1

I Java er det også noe som kalles array, som er en liste man ikke kan endre størrelse på. Selv om syntaksen for array’er minner veldig om Python sin syntaks for lister og på mange måter er penere og lettere, anbefaler vi likevel å bruke ArrayList. Grunnen er først og fremst fordi array’er i Java virker dårlig sammen med generiske typer, som vi kommer til senere; når det i tillegg ikke er mulig å endre størrelsen på arrays, blir derfor ArrayList konseptuelt sett nærmere det vi kjenner som en liste i Python. ArrayList er også anbefalt i praksis.

Vi kommer likevel borti array’er i Java, fordi dette ofte er den type liste som benyttes som parametertype eller returtype for metoder i Java sitt standardbibliotek. Du kan lese mer i kursnotater om array og lister.

Betingelser

Betingelser fungerer omtrent på samme måte i Python og Java. Merk at betingelsene alltid må være inni paranteser i Java, mens de ikke trenger det i Python.

# Python
x = 42
if x < 10:
    print("x<10")
elif x < 20:
    print("10<=x<20")
else:
    print("x>=20")
 
// Java
int x = 42;
if (x < 10) {
  System.out.println("x<10");
} else if (x < 20) {
  System.out.println("10<=x<20");
} else {
  System.out.println("x>=20");
}

Løkker

While-løkker

fungerer likt i Python og Java.

# Python
x = 0
while x < 10:
    print(x)
    x += 1
 
// Java
int x = 0;
while (x < 10) {
  System.out.println(x);
  x += 1;
}

For-each-løkker

Java har to forskjellige typer for-løkker. For-løkker over en samling med elementer, for eksempel over elementene i liste, skrives omtrent på samme måte som i Python. Dette kalles en for-each-løkke i Java.

# Python
a = ["ha", "he", "hi", "ho"]


for s in a:
    print(s)
 
// Java
ArrayList<String> a = new ArrayList<>(
    Arrays.asList("ha", "he", "hi", "ho")
);
for (String s : a) {
  System.out.println(s);
}

Standard for-løkker

Java har også det vi kaller en «standard» for-løkke. Det nærmeste motstykket til denne løkken i Python er en for-løkke over et range-objekt.

# Python
for i in range(10, 100, 5):
    print(i)
 
// Java
for (int i = 10; i < 100; i += 5) {
  System.out.println(i);
}

Standard for-løkke er vanlig for å iterere over indekser i en liste, eller for å iterere et gitt antall ganger, og spiller ofte samme rolle som en for-løkke over et range-objekt gjør i Python – selv om den egentlig er langt mer fleksibel.

Den standard for-løkken har tre setninger skilt med ; samlet på én linje. I eksempelet over:

En standard for-løkke er like fleksibel som en while-løkke. Faktisk er en for-løkke med en tom initialiseringssetning og en tom oppdateringssetning nøyaktig det samme som en while-løkke.

// Med while-løkke
int num = 1;
int count = 0;
while (count < 9) {
  if (num % 7 == 0) {
    count++;
  }
  num++;
}
System.out.println(num - 1);
// Med identisk for-løkke
int num = 1;
int count = 0;
for (;count < 9;) {
  if (num % 7 == 0) {
    count++;
  }
  num++;
}
System.out.println(num - 1);

Vi kan også utnytte alle setningsdelene i for-løkken til å skrive samme kode litt mer kompakt. Legg merke til vi bruker count i initialiseringssetningen og betingelsen, men endrer num i oppdateringssetning. Dette er litt utradisjonelt, men fullt mulig med standard for-løkker.

// Bruker count = 0 som initialsieringssetning, og num++ som oppdateringssetning
int num = 1;
for (int count = 0; count < 9; num++) {
  if (num % 7 == 0) {
    count++;
  }
}
System.out.println(num - 1);

Evig løkke, break og continue

Break og continue virker på samme måte som i Python. For å lage en evig løkke, bruker vi en standard for-løkke med tom betingelse (eventuelt er det også fullt mulig å bruke en while(true) -løkke)

# Python
i = 0
while True: # evig løkke
    i += 1
    if i < 10:
        continue

    print(i)
    if i > 20:
        break

 
// Java
int i = 0;
for (;;) { // evig løkke
  i++;
  if (i < 10) {
    continue;
  }
  System.out.println(i);
  if (i > 20) {
    break;
  }
}

Oppslagsverk

Oppslagsverk kalles for map i Java og tilsvarer omtrent et dictionary i Python. Java har flere ulike typer oppslagsverk, men HashMap er det som ligner mest på Python sitt dict. De mest fremtredene forskjellene er at

# Python
d = {}
d['a'] = 1
d['b'] = 2
for key in d:
    print(key, d[key])
 
// Java
HashMap<String, Integer> d = new HashMap<>();
d.put("a", 1);
d.put("b", 2);
for (String key : d.keySet()) {
  System.out.println(key + " " + d.get(key));
}

Mengder

Java har flere ulike typer mengder, men HashSet er det som ligner mest på Python sitt set. De mest fremtredene forskjellene er at

# Python
x = set()
x.add("Foo")
x.add("Bar")
if "Foo" in x:
    print("Foo er i x")

for e in x:
    print(e)
 
// Java
HashSet<String> x = new HashSet<>();
x.add("Foo");
x.add("Bar");
if (x.contains("Foo")) {
  System.out.println("Foo er i x");
}
for (String e : x) {
  System.out.println(e);
}

Metoder og funksjoner

I Java kaller man vanligvis funksjoner for metoder.

Forskjellen mellom begrepene metode og funksjon kommer litt an på øyet som ser:

  • En programmerer vil gjerne si at en metode er en funksjon som virker på et objekt. I dette perspektivet er metoder en undergruppe av funksjoner.
  • En matematiker vil si at begrepet funksjon innebærer at det ikke finnes sideeffekter, bare returverdi; og at en funksjon alltid vil gi samme returverdi såfremt argumentene er de samme. I en kontekst hvor alle navngitte prosedyrer med input og potensielt en returverdi kalles for metoder (slik som i Java) kan dette perspektivet la oss tenke på funksjoner som en undergruppe av metoder.

Fordi alle funksjoner i Java teknisk sett er knyttet til et objekt gir ikke den første distinksjonen oss noen særlig merverdi, og begrepene funksjon og metode blir i praksis synonymer. Det kan derfor gi mening å forholde seg til matematikernes perspektiv – men vær i så fall bevisst på at dette er et perspektiv som ikke er spesielt vanlig i programmeringssammenheng.

Man definerer en metode ved å angi

# Python

def add(a, b):
    return a + b



print(add(1, 2))

 
// Java
public class Foo {
  static int add(int a, int b) {
    return a + b;
  }

  public static void main(String[] args) {
    System.out.println(add(1, 2));
  }
}