25

I just encountered this "bug", but I'm not sure if this is intended: Code:

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
}

In the first example someMethod is being executed on the swing Thread, but in the second example it is not, although it should be in my opinion.

Is this a bug or is this intended?

Lii
  • 11,553
  • 8
  • 64
  • 88
RoiEX
  • 1,186
  • 1
  • 10
  • 37
  • 4
    It looks like method reference needs proper object or class as first argument, not some way to obtain it, so method is invoked in main thread which breaks assertion. Without assertion result of method would be used to create `methodResult::toString` which is equivalent of `()->methodResult.toString()`. – Pshemo Jun 14 '17 at 11:45
  • 18
    Where is the bug? In the second call, `someMethod()` itself is evaluated in the `main` body (thus, in the `main` thread), and then, a reference to the `toString` method of the resulting object is passed to `invokeLater`. – RealSkeptic Jun 14 '17 at 11:47
  • 4
    See also [What is the equivalent lambda expression for `System.out::println`](https://stackoverflow.com/a/28025717/2711488)… – Holger Jun 14 '17 at 12:56

2 Answers2

62

To me it seems like a misunderstanding on your side

The first line is like saying: "Ok, Swing, what I want you to invokeLater is someMethod().toString()". So Swing executes it

The second line is like saying: "Ok, Swing, what I want you to invokeLater is the method toString() of the object returned by the method someMethod()". A someMethod() method that I am executing right now

So the result is completely logical to me

Just keep in mind that before evaluating a function (in this case invokeLater) Java needs to evaluate all arguments. So in the first case Java evaluate a lambda function (no need to execute it) and in the second case it encounters a method invocation so it needs to execute it

Litsher - Nathan
  • 265
  • 1
  • 5
  • 18
Alberto S.
  • 7,409
  • 6
  • 27
  • 46
  • The question is then: Why doesn't java allow statements like `SwingUtilities.invokeLater(this::someMethod().toString);`? – RoiEX Jun 14 '17 at 11:56
  • 11
    You're making several mistakes there. Beware that you can't use `this` in an static context. Also if you're using method reference you don't put the `()` after the method name (so it would be `this::someMethod`). Finally, Java 8 allows method reference but what you're trying to use is calling a method on the object resulted of a method reference (which is not allowed) – Alberto S. Jun 14 '17 at 12:02
  • 7
    Summing up, if you need to invoke more than one method you will need a lambda function – Alberto S. Jun 14 '17 at 12:02
  • @RoiEX because the correct syntax is `this.someMethod()::toString` – user253751 Jun 15 '17 at 00:01
  • @immibis `this.someMethod()::toString` is as correct as `someMethod()::toString`. The `this` is implicit so if you add it is just for verbosity purposes – Alberto S. Jun 15 '17 at 06:25
8

This is not related to Swing, it's what happens when using method references and lambdas behind the scenes.

A simpler example:

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;
    }
}

You will get a single init output here; notice that there is no terminal operation for the stream.

Eugene
  • 117,005
  • 15
  • 201
  • 306