3

Say we have

class A {
   B method1 (C c) { ... }
}

What's the type of method1 as a method reference?

This is, what's the method signature of foo() for this call?:

foo (A::method1);

Note:

I've defined an interface

interface I {
   B m (A a, C c);
}

and it seems it is right to declare foo as

void foo (I i) { }

(since it matches the call foo(A::method1) --inside foo you have to write b=i.m(a,c) to mean b=a.method1(c)).

Is this the only way to go?

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
cibercitizen1
  • 20,944
  • 16
  • 72
  • 95

4 Answers4

3

Method references and lambdas are target-typed to match SAM interfaces. In your case it has to match I because that's the type of foo's argument.

Community
  • 1
  • 1
the8472
  • 40,999
  • 5
  • 70
  • 122
1

Informal explanation:

Remember that method references can be thought of as an alternate syntax for some lambdas, and that lambdas all implement one or more functional interfaces.

And remember that lambdas are (currently) a way to write some single-method anonymous classes in a much more concise form; thus, lambdas can always be reduced to a anonymous class (but not always the other way around).

So

foo (A::method1);

can become

foo ((c) -> <body>);

which can be reduced to the anonymous class:

foo (new Function<B>() {
    <body>
});

which is an anonymous class that is a subtype of Function

Method references are thus anonymous subtypes of whatever functional interface they represent, and so the type of the parameter has to be the corresponding functional interface or one of its supertypes.


The JLS actually has a section titled "15.13.2. Type of a Method Reference", but the wording might be a bit obtuse. In short:

A method reference can be used as a type T if:

  • T is a functional interface type (section 9.8)
  • The functional interface's abstract method matches the compile-time type of the method reference
  • Either:
    • The reference returns void
    • The reference returns a type that can be assigned to the corresponding type of the functional interface, e.g. A method reference has to return Number or a subtype to be compatible with `B foo(Function f)
awksp
  • 11,764
  • 4
  • 37
  • 44
  • it's not equivalent to anonymous inner classes because lambdas do not capture $this as a field – the8472 Sep 28 '15 at 22:28
  • @the8472 Ah, didn't know about that. Took a stab at a fixing it, hopefully it isn't wrong. Thanks for pointing that out! – awksp Sep 28 '15 at 22:36
1

Instead of declaring the new interface you may use an existing BiFunction:

void foo(BiFunction<? super A, ? super C, ? extends B> fn) {
    ...
    B b = fn.apply(a, c);
    ...
}
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
0

I am admitting here that this question is too complex for me to answer without trying, but I have tried to understand your question hence providing my understanding of this situation - Your approach is fine. Basically you are doing the following things here -

  1. Using shorthand syntax for having an anonymous class which implements the functional interface - which is fine.
  2. Let the compiler know to use A::method1 as the implementation of I.m - which is fine as well.
  3. Because A::method1 is already existing you can use method reference instead of lambda expression - - which is fine as well.
  4. Passing of actual parameter c which is done in the body of foo method in the line - b=i.m(a,c). The actual parameter used here for c should get passed to A::method1.
  5. Object a in a.method1 remains arbitrary as per java 8 specification for this syntax as you specifying Class::Method and not Instance::Method
Chesser
  • 455
  • 3
  • 9