为什么可以在线程类中使用Lambda表达式?

为什么可以在线程类中使用Lambda表达式?,lambda,java-8,Lambda,Java 8,我正在学习Lambda表达式,我知道可以在可运行函数接口和线程类中使用它们 我试图创建自己的: package tests; public interface Blabla { public void doStuff(); } 这个简单的测试: Blabla bla = () -> System.out.println("Lol"); 然后我试着做一个像线一样的类 package tests; public class Dodo implements Blabla {

我正在学习Lambda表达式,我知道可以在可运行函数接口和线程类中使用它们

我试图创建自己的:

package tests;

public interface Blabla
{
    public void doStuff();
}
这个简单的测试:

Blabla bla = () -> System.out.println("Lol");
然后我试着做一个像线一样的类

package tests;

public class Dodo implements Blabla
{
    public void doStuff()
    {
        // TODO Auto-generated method stub

    }
}
而这并没有编译:

 Dodo dodo = () -> System.out.println("LoL");
我找不到任何地方解释如何创建一个允许使用lambda表达式的类

线程如何允许在不是接口的情况下使用Lambda表达式

线程如何允许在不是接口的情况下使用Lambda表达式

我认为您对Thread类如何使用lambdas有点困惑。lambda不会转换为Thread类本身的实例;取而代之的是,lambda被转换成一个子类Runnable functional interface的实例,然后该子类被传递给一个已被调用的线程构造函数

因此,当你这样做的时候:

new Thread(() -> System.out.println("Lol")).start();
lambda未转换为您正在创建的线程对象。相反,它被转换为传递给线程构造函数的可运行对象

如果将lambda与Thread对象分开,则更清晰:

Runnable temp = () -> System.out.println("Lol");

new Thread(temp).start();
考虑线程执行函数而不是函数可能会有所帮助。您必须保持线程正在执行的函数与线程本身不同

请注意,lambda转换为对象的方式是一个实现细节,可能会在未来的Java版本中发生更改(Java 1.8.0_11是编写此答案时的最新版本)。见下面斯图尔特·马克斯的评论


我找不到任何地方解释如何创建一个允许使用lambda表达式的类

如果我理解正确,您希望编写一个类,以便可以基于该类创建lambda

不幸的是,你不能。查看Java语言规范,第15.27节:

lambda表达式的求值产生函数接口的实例(§9.8)

这里的关键点是lambda是接口的一个实例。因此,您不能使用lambda直接对类进行子类化。Brian Goetz在JDK lambda dev邮件列表中的电子邮件中可以找到这一点的基本原理(感谢Stuart Marks指出这一点!);简而言之,这个决定至少部分是为了让Java能够继续朝着新的方向发展


您可以编写一个类,其构造函数和/或方法将一个或多个函数接口作为参数,通过传递函数接口的实例(如Thread类),可以将该类与lambda一起使用。目前(据我所知),这已经非常好了。

lambda表达式只是编写匿名类的一种简短形式,通过使用lambda表达式,我们可以在任何需要的地方声明方法,而不需要任何名称

编写匿名类对象的唯一目的是重写其方法并提供我们自己的功能,但对于单个方法,我们始终需要声明该类。使用lambda表达式,我们可以消除匿名类的声明,并且可以简单地提供方法的定义

例如,我们使用下面的代码创建一个线程

Runnable runnable = new Runnable() {
  @Override
  public void run() {
  System.out.println("Running thread using anonymous class");
  }
};
Thread t = new Thread(runnable);
但是有了lambda表达式,我们可以很容易地说

Runnable runnable = () -> System.out.println("Running thread using lambda expression");
Thread t = new Thread(runnable);

因此,通过编写
Runnable Runnable=()->System.out.println(“使用lambda表达式运行线程”)
我们告诉Java创建一个类型为
Runnable
的匿名对象,并为
run()
方法提供了主体

Lambda表达式只适用于
功能接口
(一个只有一个方法的接口),因为我们正在动态地为该方法提供主体。所以,如果一个接口有多个方法,并且允许我们在其上创建lambda,那么编译器将如何识别这个提供的方法体所属的方法

Lambda表达式和匿名类之间的区别如下

  • 匿名类对象在编译后创建一个单独的类文件,从而增加jar的大小,而在编译后,Lambda表达式变为invokedynamic,这是动态语言实现
  • 我们可以使用这个关键字来表示lambda表达式中的当前类,而对于匿名类,这个关键字表示特定的匿名类
  • 对于Lambda表达式,我们只需要提供函数体,而对于匿名类,我们需要编写冗余类定义

您可以阅读更多内容。

我正在考虑添加一个部分,解释为什么一个类的lambda没有意义,但意识到我对该领域的知识不够扎实,无法提供令人满意的解释,我可能提供的任何解释都可能是完整的。如果你想听听我的猜测,我可以加上它。啊,我不知道线程有一个带有Runnable^^的构造函数,谢谢,这消除了我的疑虑:)我还认为类的lambda是不一致的,这就是线程让我困惑的原因。我会读到你的猜测,当然,没问题!当有疑问时,使用IDE检查代码;当您将鼠标悬停在某个对象上时,您可以从IDE显示的类型中学到很多东西。补充了我的猜测,希望能有所帮助。一旦你熟悉java,你应该考虑学习一种功能语言(Haskell似乎很受欢迎;我的学校教OCAML)。这是一个完全不同的视角,至少在这一案例中,它给出了Java的lambda实现中所做决定背后的一些推理。@Xkynar&user3580294:不允许lambda实现抽象类的理由如下所示。@user3580294足够接近了,我想。:-)从语义上讲,lambda希望成为函数,但即使在Java8中也只有primi
Thread t = new Thread(() -> System.out.println("Running thread using lambda expression"));