// outline of a proposed Classifier API // https://bugs.openjdk.org/browse/JDK-8357674 import java.util.*; import java.util.function.*; interface ClassifierDemo { interface Classifier { /** * Return an index which classifies the given value, or returns -1. * @parameter value the value to classify * @returns -1 if the value is unclassifiable, else a positive number */ int indexOf(T value); static Classifier of(Class cls, List values) { // we can do much better here return linearSearchClassifier(List.copyOf(values)); } } static Classifier linearSearchClassifier(List values) { return values::indexOf; } static void main(String... av) { var values = List.of("no", "yes"); Classifier cfr1 = values::indexOf; test("cfr1", cfr1); var cfr2 = Classifier.of(String.class, List.of("no", "yes")); test("cfr2", cfr2); var cfr3 = (Classifier) s -> switch (s.length()) { case 2 -> (s.equals("no") ? 0 : -1); case 3 -> (s.equals("yes") ? 1 : -1); default -> -1; }; test("cfr3", cfr3); var cfr4 = (Classifier) s -> { char c0 = s.length() > 0 ? s.charAt(0) : 0; if (c0 == 'y') return s.equals("yes") ? 1 : -1; // If we get s="yo", linear search will try "no", // but this logic rejects "yo" in the first line. return (s.equals("no") ? 0 : -1); }; test("cfr4", cfr3); // Output: // cfr1: 1 0 -1 // s.b. 1 0 -1 // cfr2: 1 0 -1 // s.b. 1 0 -1 // cfr3: 1 0 -1 // s.b. 1 0 -1 // cfr4: 1 0 -1 // s.b. 1 0 -1 enum Suit { CLUB, SPADE, HEART, DIAMOND } Classifier ecfr = Suit::ordinal; System.out.println(ecfr.indexOf(Suit.HEART));// => 2 } static void test(String name, Classifier cfr) { System.out.printf("%s: %s %s %s // s.b. 1 0 -1\n", name, cfr.indexOf("yes"), cfr.indexOf("no"), cfr.indexOf("maybe")); } } /* --- Further thoughts on using classifiers to compile enum switches. Similar points apply to string-switches and even (some) pattern-switches. If you use a link-time setup (either to final, or indy, or condy), you can build an `EnumClassifier` object. That object can contain a field `@Stable final int[] table`. As long as the table elements are not zero, everything is constant foldable. The input to the table can be the enum ordinal, and the output of the table is a tableswitch key. ``` switch (e) { case HEART -> foo(); case CLUB -> bar(); } ``` ⇒ ``` @Synthetic private static final EnumClassifier D$12 = EnumClassifier.of(Suit.class, Suit.HEART, Suit.CLUB); switch (D$12.idexOf(e)) { case 0 -> foo(); //case HEART case 1 -> bar(); //case CLUB } ``` If there is a stable `int` array in the guts of the classifer object, be sure to correct by +1/-1 somewhere to avoid storing zero values in the array. So for `enum Suit { CLUB, SPADE, HEART, DIAMOND }` (in that order), then the internal table in this case might be `int[]{ 1, -1, 2, -1 } }`, with -1 entries for spade and diamond. Inlining the API points gets what the JIT sees: ⇒ ``` @Synthetic private static final EnumClassifier D$12 = new EnumClassifier(Suit.class); static { D$12.table = new int[Suit.value().length]; //init private final Arrays.fill(D$12.table, -1); //non-zero default label D$12.table[HEART.ordinal()] = 1; //switch label D$12.table[CLUB.ordinal()] = 2; //switch label } int $tem = e.ordinal(); int $index = -1; if ($tem 0) $index-1; } switch ($index) { case 0 -> foo(); //case HEART case 1 -> bar(); //case CLUB } ``` For this purpose, a classifier which is allowed to leave a gap at zero, might be preferable. It would avoid the instructions which test and decrement $index. The bounds check on the array would be needed, if the enum is allowed to create new instances because of dark reflection magic. */