razzi.abuissa.net

Razzi's guide to Java

2024-03-03

Ok I’m not really a Java expert… it’s pretty verbose and fits mostly the same use cases as Python… but I still come across it frequently.

Ironically, I haven’t written a guide to Python, which I know much better; I guess I know enough that I feel like I wouldn’t do it justice to write a short guide.

Anyways Java and its JVM is the default runtime of Clojure, and the “write once, run anywhere” ideal of Java is pretty cool. And as we’ll see, the tooling is good.

jshell

Most people don’t think “repl” when they think Java. They think “IDE”. But I don’t like IDEs and I like repls. Fortunately Java has a good repl built in. It’s called jshell.

It’s included in Java since version 9 which came out in 2017.

$ jshell
|  Welcome to JShell -- Version 21.0.2
|  For an introduction type: /help intro

jshell>

variables

Declare variables in java with the type like so:

jshell> int x = 3
x ==> 3

jshell> x + 4
$2 ==> 7

Or let java figure out the type using var (since Java 10):

jshell> var y = 5
y ==> 5

However var doesn’t always work.

jshell> var xs = {1, 2, 3}
|  Error:
|  cannot infer type for local variable xs
|    (array initializer needs an explicit target-type)
|  var xs = {1, 2, 3};
|  ^-----------------^

People tend to not use var in production code anyways.

The type syntax for arrays is:

jshell> int[] xs = {1, 2, 3}
xs ==> int[3] { 1, 2, 3 }

The classic “hello world” in the jshell is:

jshell> System.out.println("Hello world")
Hello world

That’s verbose!!

compiling Java code

Ok now that we know how to print “Hello world”, let’s make a program that does that.

Java is very particular about filenames… we’ll see why in a second. Let’s add our hello world to a file HelloWorld.java. We need a semicolon now :(

$ echo 'System.out.println("Hello world");' > HelloWorld.java

But when we try to compile that:

$ javac HelloWorld.java
HelloWorld.java:1: error: class, interface, enum, or record expected
System.out.println("Hello world");
^
1 error

Ok so we update HelloWorld.java to the classic Java boilerplate:

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

Now we can compile it:

$ javac HelloWorld.java
$ ls
HelloWorld.class  HelloWorld.java

As we can see, this produces HelloWorld.class. This is the Java bytecode.

We can run it with java:

$ java HelloWorld
Hello world

functional java

Java has some functional features that have been added over time.

Honestly one of the big selling points of Java is that the old stuff still works; you can write a for loop that looks like this:

jshell> int[] xs = {1, 2, 3}
xs ==> int[3] { 1, 2, 3 }

jshell> for(int i = 0; i < xs.length; i++) {
   ...>     System.out.println(xs[i]);
   ...> }
1
2
3

Or you can use a foreach style loop:

jshell> for (int x : xs) {
   ...>     System.out.println(x);
   ...> }
1
2
3

But my preferred way to go about this is to use functional programming.

Since it was added much after the fact (and Java is big on object-oriented code) this style has some quirks.

In order to call .forEach which is how to pass a function to work on an element, we can either convert the array to an ArrayList:

jshell> Arrays.asList(xs)
$12 ==> [[I@1b2c6ec2]

jshell> $12.getClass()
$18 ==> class java.util.Arrays$ArrayList

Or just declare a list from the start:

jshell> var l = List.of(4, 5, 6)
l ==> [4, 5, 6]

Now we can iterate over our list:

jshell> l.forEach(x -> System.out.println(x))
4
5
6

That -> is the lambda syntax. Cool!

Since we’re just calling a function with a single argument, you’d think we could do:

jshell> l.forEach(System.out.println)
|  Error:
|  cannot find symbol
|    symbol:   variable println
|  l.forEach(System.out.println)
|            ^----------------^

Ok it can’t access println as a function; this is where the object-oriented + functional starts to clash somewhat. I guess it could be akin to the old-style Python print statement as opposed to the Python 3 print function. Java would never do that; it’d break backwards compatibility.

Fortunately they have this syntax for accessing a method as a function:

jshell> l.forEach(System.out::println)
4
5
6

There you have it, functional Java. Had to jump through some hoops but the final result ain’t bad.