为什么invokeLater在主线程中执行?

25

我刚遇到了这个“bug”,但我不确定它是否是有意的:

public static Object someMethod(){
    assert SwingUtilities.isEventDispatchThread();
    return new Object();
}

public static void main(String[] args){
    SwingUtilities.invokeLater(() -> someMethod().toString());//First Example
    SwingUtilities.invokeLater(someMethod()::toString);//Second Example
}

在第一个示例中,someMethod 在 swing 线程上执行,但在第二个示例中却没有,在我看来,它应该有。

这是一个错误还是故意的?


4
看起来方法引用需要正确的对象或类作为第一个参数,而不是某种获取它的方式,因此方法在主线程中调用,这会导致断言失败。如果没有断言,方法的结果将被用来创建 methodResult ::toString,这相当于 () -> methodResult.toString() - Pshemo
18
错误在哪里?在第二个调用中,someMethod()本身在main主体中被评估(因此,在main线程中),然后将对结果对象的toString方法的引用传递给invokeLater - RealSkeptic
2个回答

62

在我看来,这似乎是你方面的一个误解

第一行就像是说:“好的,Swing,我希望你用invokeLater调用someMethod().toString()方法”。因此,Swing会执行它

第二行就像是说:“好的,Swing,我希望你用invokeLater调用someMethod()方法返回的对象的toString()方法”。一个我正在执行的someMethod()方法。

因此,对我来说,结果完全合理

请记住,在计算函数(在本例中为invokeLater)之前,Java需要评估所有参数。因此,在第一种情况下,Java评估一个lambda函数(无需执行它),而在第二种情况下,它遇到了一个方法调用,因此需要执行它


那么问题是:为什么Java不允许像SwingUtilities.invokeLater(this::someMethod().toString);这样的语句? - RoiEX
11
你犯了几个错误。要注意在静态上下文中无法使用 this。另外,如果你正在使用方法引用,则不需要在方法名称后面加上 ()(因此应该是 this::someMethod)。最后,Java 8允许使用方法引用,但是你试图调用一个在方法引用的对象上调用方法(这是不允许的)。 - Alberto S.
7
总之,如果您需要调用多个方法,您将需要一个lambda函数。 - Alberto S.
@RoiEX 因为正确的语法是 this.someMethod()::toString - user253751
@immibis this.someMethod()::toStringsomeMethod()::toString 一样正确。this 是隐式的,所以如果你添加它只是为了增加可读性。 - Alberto S.

8

这与Swing无关,而是在使用方法引用和lambda表达式时发生的事情。

一个更简单的例子:

public static void main(String[] args) {
    Stream.of(1, 2, 3).map(initMapper()::inc);

    Stream.of(1, 2, 3).map(x -> initMapper().inc(x));
}

private static Mapper initMapper() {
    System.out.println("init");
    return new Mapper();
}

static class Mapper {

    public int inc(int x) {
        return x + 1;
    }
}

您将在这里获得一个单一的init输出;请注意,流没有终止操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接