Java method reference to shared parameter method

I am trying to make a method reference to a method that has a common parameter specified in a class declaration. So I have:

public interface IExecutable<P extends IParameter> {

    void execute(P parameter);

}

public class Parameter implements IParameter {

    public void childSpecific() {
    ...
    }
}

public class TestClass {
    ...
    //somewhere in the code
    public void foo(Parameter parameter) {
        parameter.childSpecific();
    }

    public void test() {
        IExecutable<?> executable = this::foo; //compilation error
        // The type TestClass does not define inner(IParameter) that is applicable here
        executable.execute(new Parameter()); //compilation error as well
        // The method execute(capture#4-of ?) in the type IExecutable<capture#4-of ?> is not applicable for the arguments (Parameter)
    }
    ...
}

      

Specifically, that I don't know of a specific generic executable type here. Using

IExecutable<Parameter> = ...

      

solves the problem immediately, but this is not possible for case.

It is clear that I am doing something wrong. But how do you make it work?

thank.

+3


source to share


3 answers


In this case, foo is not written to handle IParameter

anything other than Parameter

. You can assign a reference to foo to a variable of type IExecutable<? extends IParameter>

, however this means that it is an executable file that handles an unknown type IParameter

(in this case Parameter

). Since the specific subtype is unknown, it would be non-synthetically safe to pass any subtype IParameter

to its execution method, since you don't know what it can handle in this area!

You need a different type variable instead of using capture (?). This way you can specify that IParameter

which you are passing is the same type as IParameter

which the executable is taking. You can introduce this with a new method as I do below:



public class TestClass {
  public static void foo(Parameter parameter) {
    parameter.childSpecific();
  }

  public static void main(String args) {
    execute(TestClass::foo, new Parameter());
  }

  public static <P extends IParameter> void execute(
        IExecutable<P> executable, P param) {
    executable.execute(param);
  }
}

      

+3


source


The type parameter P

in your interface IExecutable

is subtyped limited IParameter

. Consider these two subtypes:

class Parameter implements IParameter { ... }
class AnotherParameter implements IParameter { ... }

      

Now, IExecutable<?>

no more specifically refers to the above limitation. In fact, it ?

states that it is associated with an unknown subtype IParameter

, which could be Parameter

or AnotherParameter

(in my example).

With this variable declaration, you run into the two problems you mentioned.

  • Your method foo(Parameter)

    does not meet the more general constraint IExecutable<?>

    . As seen above, such an executable can be linked to AnotherParameter

    , which will clearly violate the method signature foo

    .

  • Even if it fits, it cannot be used the way you do. The compiler does not know what type it was mapped to ?

    . The only thing he knows is that it must be a subtype IParameter

    , but unknown. This means that the assertion is executable.execute(new Parameter())

    invalid (as is executable.execute(new AnotherParameter())

    ). The only parameter you can pass to execute

    is null

    .



Conclusion: Point 1 can be solved by declaring a variable executable

with a type IExecutable<? extends Parameter>

. This matches the method signature foo

. But point 2 still doesn't allow calling execute

.

The only thing you can do is declare the variable as

IExecutable<Parameter> executable = this::foo;

      

This will compile and resolve the call

executable.execute(new Parameter());

      

+1


source


This line fails in java type inference

IExecutable<?> executable = this::foo;

      

Let's look at it this way.

IExecutable<?> executable = p->this.foo(p);

      

To compile it java needs to know the value foo(p)

. Before java8, the type of an expression builds on the types of subexpressions; here the type p

must be known 1st for permission foo

. But the type is p

not specified, it needs to be inferred from the surrounding context. Here is the context IExecutable<? extends IParameter>

, but it is p

deduced on IParameter

- and the method foo(Iparameter)

does not exist.

In general, type inference faces a dilemma, is it inference from top to bottom or bottom to top? Java8 defines an extremely complex procedure for this, which is humanly impossible to understand :)

Workarounds: specify the type p

IExecutable<?> executable = (Parameter p)->this.foo(p);

      

or specify a more specific type of goal

IExecutable<?> executable = (IExecutable<Parameter>)p->this.foo(p);

IExecutable<?> executable = (IExecutable<Parameter>)this::foo;

      

If you ask the language designers, they'll think this is all completely obvious ... but the best way for a programmer is probably to just try different things until it works, than to study the actual language specification.

0


source







All Articles