有没有办法在 lambda 表达式中显式定义泛型参数类型?

Posted

技术标签:

【中文标题】有没有办法在 lambda 表达式中显式定义泛型参数类型?【英文标题】:Is there a way to define a generic parameter type explicitly in a lambda expression? 【发布时间】:2015-08-02 11:57:42 【问题描述】:

我这里有一个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 ->  ;

【问题讨论】:

【参考方案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 主体的效果

【讨论】:

以上是关于有没有办法在 lambda 表达式中显式定义泛型参数类型?的主要内容,如果未能解决你的问题,请参考以下文章

是否执行了视图列集中的所有函数,即使它们没有在查询中显式引用?

在 XML 查询中显式定义数据类型,以便 s-s-rS 从 WCF 服务中识别

Lambda 表达式和泛型仅在方法中定义

使用 lambda 而不是显式匿名内部类时的不同泛型行为

如何在 lapply (R) 中显式定义附加参数

sequelize抛出错误方言需要在express js中显式定义