【问题标题】:Is there a way to define a generic parameter type explicitly in a lambda expression?有没有办法在 lambda 表达式中显式定义泛型参数类型?
【发布时间】:2023-03-30 01:20:01
【问题描述】:

我这里有一个Handler 类,它应该处理某种类型的Events:

public interface Handler<E extends Event>
{
    public void handle(E event);

    @SuppressWarnings("unchecked")
    public default Class<E> getEventType()
    {
        for(Method method: this.getClass().getDeclaredMethods())
        {
            if(method.getName().equals("handle")) return (Class<E>)method.getParameterTypes()[0];
        }
        throw new NullPointerException("Couldn't find the 'handle' method in this handler.");
    }
}

如您所见,默认情况下,当您执行 getEventType() 时,它会通过返回 handle() 方法的第一个参数类型(而不是 Handler显式返回它)。这适用于以下 JUnit 测试:

public static class EmptyEvent extends Event
{
    public void test() { }
}

public static Handler<EmptyEvent> genericHandler = new Handler<EmptyEvent>()
{
    @Override
    public void handle(EmptyEvent event)
    {

    }
};

@Test
public void testEventGenerics()
{
    //prints the name of EmptyEvent
    System.out.println(genericHandler.getEventType());
}

Intellij IDEA 告诉我可以将 genericHandler 简化为 lambda 表达式,所以我这样做了:

public static class EmptyEvent extends Event
{
    public void test() { }
}

public static Handler<EmptyEvent> genericHandler = event -> { };

@Test
public void testEventGenerics()
{
    //prints the name of the base Event class
    System.out.println(genericHandler.getEventType());
}

但是,测试会打印出Event 的名称,而不是EmptyEvent

所以我的问题是,有没有办法明确定义 lambda 表达式的泛型参数类型?

我尝试做这样的事情,但它什么也没做(也是一个错误)

public static Handler<EmptyEvent> genericHandler = (EmptyEvent)event -> { };

【问题讨论】:

    标签: java generics lambda


    【解决方案1】:

    是的,您可以定义 lambda 表达式参数的类型:

    public static Handler<EmptyEvent> genericHandler = (EmptyEvent event) -> { };
    

    一般来说没有必要,因为它是由上下文暗示的。然而,它通常使代码更易于阅读。

    详情请见https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27

    【讨论】:

    • 嗯。它编译并回答了问题,但由于某种原因genericHandler.getEventType() 仍然返回Event 的名称。你知道为什么吗?
    • @Octopod 可能是因为您依赖于 getDeclaredMethods 返回不同方法的顺序,这是一个禁忌。
    • @immibis 除了我很确定只有一个 handle() 方法(我定义的那个),不是吗?你是说还有handle(Event)
    • @Octopod 是的;这是一种桥梁方法。我不知道它是否会从getDeclaredMethods返回,但显然它是。
    • @immibis,我更改了getEventType() 以打印出每个名为handle 的方法。 handle(Event) 是它使用getDeclaredMethods() 找到的唯一方法。 getMethods() 返回相同的东西。
    【解决方案2】:

    您正在对 lambda 评估的对象的类做出假设,我认为这些假设是不成立的。 lambda 覆盖来自Handler 的方法handle() 就足够了,并且在运行时,采用Eventhandle() 方法足以覆盖来自Handlerhandle(),擦除后采用@987654327 @。无法保证 lambda 必须有一个 handle() 方法,可以自省说它需要 EmptyEvent

    【讨论】:

      【解决方案3】:

      以下变量可能都指向同一个对象!

      Handler<EventA> handlerA = event -> { };
      Handler<EventB> handlerB = event -> { };
      
      Consumer<String> consumer1 = string -> {}
      Consumer<Integer> consumer2 = integer-> {}
      

      您无法真正推断出 lambda 表达式的确切运行时类型;唯一的保证是 lambda 主体在运行时工作。

      http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27.4

      [runtime] 方法的主体具有评估 lambda 主体的效果

      【讨论】:

        猜你喜欢
        • 2016-07-20
        • 2014-04-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多