1

I wrote this code as a solution on another Question:

record Person( String name , int age ) { }
List < Person > persons =
        List.of(
                new Person( "Alice" , 52 ) ,
                new Person( "Bob" , 23 ) ,
                new Person( "Carol" , 39 )
        );

List < Person > sorted =
        persons
                .stream()
                .sorted( Comparator.comparing( Person :: age ) )
                .toList();

This code runs successfully on Java 17.

persons.toString() = [Person[name=Alice, age=52], Person[name=Bob, age=23], Person[name=Carol, age=39]]

sorted.toString() = [Person[name=Bob, age=23], Person[name=Carol, age=39], Person[name=Alice, age=52]]

Upon further thought, I am surprised this code works. I pass a method reference for the accessor method (“getter”) on the record to Comparator.comparing call.

Comparator.comparing( Person :: age )

… but age member field, and the return type of this method, are both int primitive, not an object.

This leaves me puzzled as to why this works, because I would think the comparator requires objects, not primitives.

Is there some auto-boxing at play here? If so, exactly where? What triggers the compiler or runtime to perform auto-boxing in this code?

I am hoping for a narrow and precise answer rather than same hand-waving. The hand-waving was already done when I authored the code! So, now, I am not asking out of practicality, as clearly the code is intuitive and the code works. The language-lawyer tag is indeed appropriate here, as I am asking about the “intricacies of formal or authoritative specifications of programming languages”.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • 3
    You are right, you should (probably) use `Comparator.comparingInt()` to avoid the needless boxing. – Ole V.V. Mar 25 '22 at 07:14

1 Answers1

1

I'll leave as an exercise for the reader that the resolution process for Person::age identifies the potentially-applicable method int age() as an exact method reference expression. Having identified the target method, from JLS 15.13.2:

A method reference expression is congruent with a function type if both of the following are true:

  • The function type identifies a single compile-time declaration corresponding to the reference.
  • One of the following is true: […]
    • The result of the function type is R, and the result of applying capture conversion (§5.1.10) to the return type of the invocation type (§15.12.2.6) of the chosen compile-time declaration is R' (where R is the target type that may be used to infer R'), and neither R nor R' is void, and R' is compatible with R in an assignment context.

The function type here is Function<? super T, ? extends U>. T is clearly Person, and the signature of comparing requires some U extends Comparable<? super U>.

The type int is compatible with Integer in an assignment context and then subsequently also compatible with Comparable<Integer> (JLS 5.2):

Assignment contexts allow the use of one of the following: […]

  • a boxing conversion followed by a widening reference conversion
chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152