简体   繁体   English

Java方法引用具有泛型参数的方法

[英]Java method reference to a method with generic parameter

I'm trying to make a method reference to a method which have a generic 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)
    }
    ...
}

It's specific that I don't know the concrete generic type of the executable here. 具体是我不知道可执行文件的具体泛型类型。 Using 运用

IExecutable<Parameter> = ...

solves the problem immediately, but it's impossible for the case. 立即解决问题,但案件不可能。

Clearly, I'm doing something wrong. 显然,我做错了什么。 But how to make it work? 但是如何让它发挥作用?

Thx. 谢谢。

In this case, foo is not written to handle any IParameter other than Parameter . 在这种情况下,不会编写foo来处理除Parameter之外的任何IParameter You could assign a reference to foo to a variable of type IExecutable<? extends IParameter> 您可以将foo的引用分配给IExecutable<? extends IParameter>的变量IExecutable<? extends IParameter> IExecutable<? extends IParameter> , however this means that it is an executable that handles some unknown type of IParameter (in this case, Parameter ). IExecutable<? extends IParameter> ,但这意味着它是一个可执行文件,可以处理一些未知类型的IParameter (在本例中为Parameter )。 Since the specific subtype is unknown, it would not be syntactically safe to pass any subtype of IParameter in to its execute method, since you don't know which it can handle within this scope! 由于特定的子类型是未知的,因此在它的执行方法中传递任何IParameter子类型在语法上是不安全的,因为你不知道它可以在这个范围内处理它!

What you need is another type variable instead of using a capture (the ?). 你需要的是另一种类型变量,而不是使用捕获(?)。 This way you can specify that the IParameter you're passing in is the same type as the IParameter the executable accepts. 这样,您就可以指定IParameter你传递的是作为同一类型IParameter可执行接受。 You could introduce this with a new method, like I'm doing 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);
  }
}

The type parameter P in your interface IExecutable is constrained to being a subtype of IParameter . 接口IExecutable的类型参数P被约束为IParameter的子类型。 Consider these two subtypes: 考虑这两个子类型:

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

Now, an IExecutable<?> is not more specific regarding the above mentioned constraint. 现在, IExecutable<?>对于上述约束并不具体。 In fact, the ? 事实上, ? states that it is bound to an unknown subtype of IParameter , which could be Parameter or AnotherParameter (in my example). 声明它绑定到一个未知IParameter子类型,它可以是ParameterAnotherParameter (在我的例子中)。

With such a variable declaration, you face the two problems you mentioned. 有了这样一个变量声明,你就会遇到你提到的两个问题。

  1. Your method foo(Parameter) does not match the more general constraint of an IExecutable<?> . 您的方法foo(Parameter)IExecutable<?>的更一般约束不匹配。 As seen above, such an executable could be bound to AnotherParameter which clearly would violate the method signature of foo . 如上所示,这样的可执行文件可以绑定到AnotherParameter ,这显然会违反foo的方法签名。

  2. Even if it matched, it cannot be used like you did. 即使它匹配,它也不能像你一样使用。 The compiler does not know to which type the ? 编译器不知道哪种类型的? actually was mapped. 实际上是映射的。 The only thing it knows: It must be a subtype of IParameter , but which one is not known. 它唯一知道的事情是:它必须是IParameter的子类型,但哪一个是未知的。 That means, the statement executable.execute(new Parameter()) is not allowed (as also executable.execute(new AnotherParameter()) ). 这意味着,不允许使用语句executable.execute(new Parameter()) (还有executable.execute(new AnotherParameter()) )。 The only parameter you are allowed to pass to execute is null . 允许传递给execute的唯一参数是null

Conclusion: Point 1 could be solved by declaring the variable executable with type IExecutable<? extends Parameter> 结论:可以通过声明类型为IExecutable<? extends Parameter>的变量executable来解决第1点IExecutable<? extends Parameter> IExecutable<? extends Parameter> . IExecutable<? extends Parameter> This matches the method signature of foo . 这与foo的方法签名匹配。 But point 2 still does not allow the call to execute . 但是第2点仍然不允许execute调用。

The only thing you can do is to declare the variable as 你唯一能做的就是将变量声明为

IExecutable<Parameter> executable = this::foo;

This will compile and allow the call to 这将编译并允许调用

executable.execute(new Parameter());

This line exposes failure in java type inference 此行暴露了java类型推断中的失败

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 meaning of foo(p) . 要编译它,java需要知道foo(p)的含义。 Before java8, the type of an expression is built on the types of sub-expressions; 在java8之前,表达式的类型建立在子表达式的类型上; here, the type of p needs to be known 1st to resolve foo . 这里,需要知道p的类型才能解析foo But the type of p is not specified, it needs to be inferred from surrounding context. 但是没有指定p的类型,它需要从周围的上下文推断出来。 Here the context is IExecutable<? extends IParameter> 这里的上下文是IExecutable<? extends IParameter> IExecutable<? extends IParameter> , and p is inferred to IParameter - and method foo(Iparameter) does not exist. IExecutable<? extends IParameter> ,并将p推断为IParameter - 并且方法foo(Iparameter)不存在。

In general, type inference faces a dilemma, does it infer from top down, or bottom up? 一般来说,类型推断面临两难选择,它是从上到下推断还是自下而上? Java8 defines an extremely complicated procedure for that, which is humanly impossible to understand:) Java8为此定义了一个极其复杂的过程,这是人类无法理解的:)

Workarounds: specify the type of p 变通方法:指定p的类型

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

or specify a more specific target type 或指定更具体的目标类型

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

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

If you ask the language designers, they'd consider all of this is quite obvious ... but a programmer's best action is probably just try different things till it works, than to study the actual language spec. 如果你问语言设计师,他们会认为这一切都很明显......但程序员的最佳动作可能就是尝试不同的东西直到它起作用,而不是研究实际的语言规范。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM