Saturday, November 05, 2022

Java 19: Guarded Patterns in Switch

Previously, I wrote about how switch statements and expressions had been enhanced to match on type patterns, and also how "guarded patterns" can be used to refine a pattern so that it is only matched on certain conditions.

In Java 19, the syntax of the guarded pattern has been changed so that instead of using &&, you need to use a when clause, as shown in the example below.

static String guardedPattern(Collection<String> coll) {
  return switch(coll) {
    case null -> 
      "Collection is null!";
    case List list 
    when list.size() > 10 ->
      "I am a big List. My size is " + list.size();
    case List list ->
      "I am a small List. My size is " + list.size();
    default -> 
      "Unsupported collection: " + coll.getClass();
  };
}

As an aside, it's worth pointing out how nulls are handled within the switch block. The default label does NOT match nulls, so you need to explicitly add a case null, otherwise you will get a NullPointerException. This is for backwards compatibility with the current semantics of switch.

Related post:
Java 17: Pattern Matching for Switch

Tuesday, May 31, 2022

Java 18: Simple Web Server

Java 18 offers an out-of-the-box simple web server (jwebserver) that serves static files only (no servlet-like functionality or CGI). This tool is useful for prototyping, ad hoc coding, and testing.

To start the server, simply run:

$ jwebserver
Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
Serving C:\Users\fahd\blog and subdirectories on 127.0.0.1 port 8000
URL http://127.0.0.1:8000/

127.0.0.1 - - [31/May/2022:10:37:31 +0100] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [31/May/2022:10:37:33 +0100] "GET /2022/ HTTP/1.1" 200 -

By default, the server binds to localhost:8000 and serves the current working directory. Every request is logged to the console.

You can change the bind address, port number, directory and logging format using the options shown below:

$ jwebserver --help
Usage: jwebserver [-b bind address] [-p port] [-d directory]
                  [-o none|info|verbose] [-h to show options]
                  [-version to show version information]
Options:
-b, --bind-address    - Address to bind to. Default: 127.0.0.1 (loopback).
                        For all interfaces use "-b 0.0.0.0" or "-b ::".
-d, --directory       - Directory to serve. Default: current directory.
-o, --output          - Output format. none|info|verbose. Default: info.
-p, --port            - Port to listen on. Default: 8000.
-h, -?, --help        - Prints this help message and exits.
-version, --version   - Prints version information and exits.
To stop the server, press Ctrl + C.

To programmatically start the web server from within a java application, you can use the SimpleFileServer.createFileServer method:

import java.net.InetSocketAddress;
import java.nio.file.Path;

import com.sun.net.httpserver.SimpleFileServer;
import com.sun.net.httpserver.SimpleFileServer.OutputLevel;

final var server = SimpleFileServer.createFileServer(
                       new InetSocketAddress(8080), 
                       Path.of("C:\\Users\\fahd\\blog"),
                       OutputLevel.VERBOSE);
server.start();

Alternatively, use HttpServer.create if you wish to pass in your own HTTP handler and filter:

import java.net.InetSocketAddress;
import java.nio.file.Path;

import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.SimpleFileServer;
import com.sun.net.httpserver.SimpleFileServer.OutputLevel;

final var server = HttpServer.create(
              new InetSocketAddress(8000),
              10,
              "/context/",
              SimpleFileServer.createFileHandler(Path.of("C:\\Users\\fahd\\blog")),
              SimpleFileServer.createOutputFilter(System.out, OutputLevel.INFO));
server.start();

Monday, January 03, 2022

Advent of Code 2021

At the end of last year, I took part in Advent of Code, a programming competition that takes place in December every year. It is an Advent calendar of programming puzzles - a new puzzle is released every day from 1-Dec to 25-Dec - and is a great way to test your programming skills and brush up on those algorithms that you don't use very often (like Djikstra's!). There were some really challenging problems and I am pleased that I managed to answer them all using Java. I actually surprised myself on a couple of them because when I first read the question, I didn't think I'd be able to do it. However, I persevered (sometimes even spending the whole day on the problem) and took insipiration from other programmers on the reddit board.

The questions are still available so I would encourage you to have a go!

Bring on Advent of Code 2022!

Saturday, January 01, 2022

fahd.blog in 2021

Happy 2022, everyone!

I'd like to wish everyone a great start to an even greater new year!

In keeping with tradition, here's one last look back at fahd.blog in 2021.

During 2021, I posted 6 new entries on fahd.blog. I am also thrilled that I have more readers from all over the world! Thanks for reading and especially for giving feedback.

Top 3 posts of 2021:

I'm going to be writing a lot more this year, so stay tuned for more great techie tips, tricks and hacks! :)

Related posts:

Saturday, September 18, 2021

Java 17: Pattern Matching for Switch

In Java 17 (released only a few days ago), Pattern Matching for switch has been introduced as a preview language feature, which allows case labels with patterns rather than just constants. Here is an example showing how you can match on type patterns:

public static String typedPatternMatching(Object o) {
  return switch(o) {
    case null      -> "I am null";
    case String s  -> "I am a String. My value is " + s;
    case Integer i -> "I am an int. My value is " + i;
    default        -> "I am of an unknown type. My value is " + o.toString();
  };
}

// Output:
> typedPatternMatching("HELLO")
"I am a String. My value is HELLO"

> typedPatternMatching(123)
"I am an int. My value is 123"

> typedPatternMatching(null)
"I am null"

> typedPatternMatching(0.5)
"I am of an unknown type. My value is 0.5"

You can also use a guarded pattern in order to refine a pattern so that it is only matched on certain conditions, for example:

public static String guardedPattern(Collection<String> coll) {
  return switch(coll) {
    case List list && (list.size() > 10) -> 
        "I am a big List. My size is " + list.size();
    case List list -> 
        "I am a small List. My size is " + list.size();
    default -> 
        "Unsupported collection: " + coll.getClass();
  };
}

If you have a Sealed Class (made a permanent language feature in Java 17), the compiler can verify if the switch statement is complete so no default label is needed. For example:

sealed interface Vehicle permits Car, Truck, Motorcycle {}
final class Car implements Vehicle {}
final class Truck implements Vehicle {}
final class Motorcycle implements Vehicle {}

public static String sealedClass(Vehicle v) {
  return switch(v) {
    case Car c -> "I am a car";
    case Truck t -> "I am a truck";
    case Motorcycle m -> "I am a motorcycle";
  };
}