Hello! I just was thinking about good samples where switch patterns could be useful. One idea I have is Arrays.deepHashCode. The current implementation uses a helper method and queries getClass():
public static int deepHashCode(Object a[]) { if (a == null) return 0; int result = 1; for (Object element : a) { final int elementHash; final Class<?> cl; if (element == null) elementHash = 0; else if ((cl = element.getClass().getComponentType()) == null) elementHash = element.hashCode(); else if (element instanceof Object[]) elementHash = deepHashCode((Object[]) element); else elementHash = primitiveArrayHashCode(element, cl); result = 31 * result + elementHash; } return result; } private static int primitiveArrayHashCode(Object a, Class<?> cl) { return (cl == byte.class) ? hashCode((byte[]) a) : (cl == int.class) ? hashCode((int[]) a) : (cl == long.class) ? hashCode((long[]) a) : (cl == char.class) ? hashCode((char[]) a) : (cl == short.class) ? hashCode((short[]) a) : (cl == boolean.class) ? hashCode((boolean[]) a) : (cl == double.class) ? hashCode((double[]) a) : // If new primitive types are ever added, this method must be // expanded or we will fail here with ClassCastException. hashCode((float[]) a); } It can be simplified with patterns: public static int deepHashCode(Object[] a) { if (a == null) return 0; int result = 1; for (Object element : a) { final int elementHash = switch(element) { case null -> 0; case Object[] arr -> deepHashCode(arr); case byte[] arr -> hashCode(arr); case short[] arr -> hashCode(arr); case char[] arr -> hashCode(arr); case int[] arr -> hashCode(arr); case long[] arr -> hashCode(arr); case float[] arr -> hashCode(arr); case double[] arr -> hashCode(arr); default -> element.hashCode(); }; result = 31 * result + elementHash; } return result; } Here we see good use of case null. One problem is the possible (though unlikely) appearance of new primitive types. In this case, the default branch will be silently taken. Well, we can add an assert: default -> { assert !element.getClass().isArray(); yield element.hashCode(); } Probably we need a sealed supertype for all arrays that permits all final primitive subtypes and non-sealed Object[] subtype? In this case, we could make a sub-switch and rely on the compiler: final int elementHash = switch(element) { case null -> 0; case AnyArray anyArray -> switch(anyArray) { case Object[] arr -> deepHashCode(arr); case byte[] arr -> hashCode(arr); case short[] arr -> hashCode(arr); case char[] arr -> hashCode(arr); case int[] arr -> hashCode(arr); case long[] arr -> hashCode(arr); case float[] arr -> hashCode(arr); case double[] arr -> hashCode(arr); // no default case! }; default -> element.hashCode(); }; Well, this is probably a huge amount of work with little benefit, though probably something similar is being baked in Valhalla? In any case, I like the new version more than the original one. With best regards, Tagir Valeev.