javahibernateannotationshibernate-mapping

Why am I able to use ReverseStringComparator.class as the value of the @SortedComparator annotation when it is not a primitive or a String?


@Entity
@Table(name = "student")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // other fields

    @ElementCollection
    @CollectionTable(name = "studentImages")
    @MapKeyColumn(name = "file_name")
    @SortComparator(reverseSort.getClass()) // Error: Attribute value must be a constant
    @Column(name = "img_desc")
    private Map<String, String> images = new TreeMap<>();

    private static Comparator<String> reverseSort = Comparator.reverseOrder();
}

For the error denoted above, I have referred these posts:

These indicate that the value for an annotation must be a compile time constant and a compile time constant can only be a primitive or string. But the error indicated above could be resolved by defining an inner class like so:

@Entity
@Table(name = "student")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // other fields

    @ElementCollection
    @CollectionTable(name = "studentImages")
    @MapKeyColumn(name = "file_name")
    @SortComparator(ReverseStringComparator.class) // Is this a compile time constant?
    @Column(name = "img_desc")
    private Map<String, String> images = new TreeMap<>();

    private class ReverseStringComparator implements Comparator<String> {
        @Override
        public int compare(String o1, String o2) {
            return o2.compareTo(o1);
        }
    }
}

Why am I able to use ReverseStringComparator.class as the value of @SortedComparator annotation when it is not a primitive or a String? Also, is there any other way than defining and using an inner class here (using @SortComparator)?


Solution

  • You can use ReverseStringComparator.class because that is a class literal, which is explicitly listed as one of the allowed values for an annotation in JLS 9.7.1 Normal Annotations (bold emphasis from source, italic emphasis mine):

    It is a compile-time error if the element type is not commensurate with the element value. An element type T is commensurate with an element value v if and only if one of the following is true:

    • T is an array type E[], and either:

      • [removed for brevity]
    • T is not an array type, and the type of v is assignment compatible (§5.2) with T, and:

      • If T is a primitive type or String, then v is a constant expression (§15.29).
      • If T is Class or an invocation of Class (§4.5), then v is a class literal (§15.8.2).
      • If T is an enum class type (§8.9), then v is an enum constant (§8.9.1).
      • v is not null.

    To highlight the specific rule that applies:

    • If T is Class or an invocation of Class (§4.5), then v is a class literal (§15.8.2).

    As shown, not only primitive or String is allowed in an annotation, but also a class literal and an enum constant.

    To answer your second question: no there is no other way to declare this using @SortComparator, as it expects a Class, and as detailed above, then only a class literal is allowed in the annotation.