难以理解Java 8 Lambda表达式

9
private ExecutorService exec = Executors.newSingleThreadExecutor(r -> {
    Thread t = new Thread(r);
    t.setDaemon(true); // allows app to exit if tasks are running
    return t ;
});

我理解执行程序的思想,但参数r让我感到困惑。我使用了:

 final ExecutorService exec = Executors.newSingleThreadExecutor(r -> {
        Thread t = new Thread(r);
        System.out.println("Class of r: " + r.getClass()+ ". r to string: " + r.toString());
        System.out.println("Class of t: " + t.getClass() +". Name of t: "+ t.getName());
        t.setDaemon(true);
        return t;
    });

进一步挖掘,结果如下:

Class of r: class java.util.concurrent.ThreadPoolExecutor$Worker. r to string: java.util.concurrent.ThreadPoolExecutor$Worker@1dc3963[State = -1, empty queue]
Class of t: class java.lang.Thread. Name of t: Thread-3

参数r被传递给Thread对象的构造函数。

  1. 简单的字母r如何表示被传递的对象是一个ThreadPoolExecutor
  2. 如果ThreadPoolExecutor没有像Thread构造函数所需的那样实现Runnable,它如何作为参数传递?

如果有人能够提供非lambda版本的代码,这对我理解会很有帮助。

1个回答

17

newSingleThreadExecutor需要一个ThreadFactory作为参数。ThreadFactory定义了一个名为newThread的方法,该方法接受一个Runnable作为参数并返回一个Thread。

如果我们明确指定r的类型,那么lambda表达式可能更容易理解:

(Runnable r) -> {
    Thread t = new Thread(r);
    return t;
}

现在更明显了,这是对newThread方法体的定义。
除此之外,由于lambda表达式被立即作为一个ThreadFactory接受的参数传递,编译器能够推断出r的类型必须是Runnable,因此它可以被省略。
没有lambda表达式,这段代码会被转化为以下匿名类的定义和实例化:
private ExecutorService exec = Executors.newSingleThreadExecutor(
    new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setDaemon(true);
            return t;
        }
    }
);

简单的字母r如何表明被传递的对象是一个ThreadPoolExecutor

r的类型是Runnable,因为lambda表达式的目标类型以这种方式定义了它的单个方法。

您看到的被传递的对象实际上是ThreadPoolExecutor$Worker,这是ThreadPoolExecutor的一个私有内部类,它实现了Runnable。

如果ThreadPoolExecutor不像Thread的构造函数所需的那样实现Runnable,它如何作为参数传递?

请参见上文(r是一个Runnable)。


谢谢,Radiodef。我现在完全理解这段代码了。我在哪里可以学习更多关于 outerclass$innerclass 类型表示法的知识 - Java 语言规范? - user465001
3
我很高兴为您解释。关于“$”符号,没有太多需要学习的了。当您在代码中引用嵌套或内部类时,使用的是“Outer.Inner”,但通常实际上被编译成一个名为“Outer$Inner”的类。 “$”符号对于所有标识符都是有效的,但通常被视为内部机制保留。 - Radiodef
再次感谢您,Radiodef! - user465001
4
有时你也会在其他类中看到美元符号,例如当你看到类名ClassContainingLambda$$Lambda$1时,它并不是一个内部类... - Holger

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