Java匿名类
什么叫匿名类
Java 的匿名类(也叫匿名内部类,因为肯定在某个类的内部)是没有名称的类。因为没有名称就无法在别的地方调用,只能在定义的时候调用了。
匿名类定义:
new SuperClass(Parameter Lists) {
// 匿名类实现部分
};
// 或者
new SuperInterface() {
// 匿名类实现部分
};匿名类是与new关联的,在创建对象的时候定义类,new后面是父类或者父接口;然后是圆括号(),里面可以是传递给父类构造函数的参数,如果父类是一个接口时,没有构造函数则是一对空括号;最后是大括号{},里面是类的定义;因为匿名类定义是一个表达式,所以它是语句的一部分,因此最后有个分号,表示语句结束。
为什么有匿名类
有时候,为了实现一个功能或者实例化一个对象,需要实现一个接口,但是接口应用的地方很多,每个地方的实现都不尽相同,而且需要实例化的地方就只有那么一两处,这个时候,如果为了这些地方,每次都声明一个类来实现接口的话,就会浪费很多空间,还得费时编译,匿名类可以在需要的地方使用接口,可以在使用的同时实现定义,这样不但节省了空间,还可以使代码更加明了。
示例
public class HelloTest {
private String text = " world";
private void hello() {
IHello ihello = new IHello() {
@Override
public void sayHello() {
System.out.print("Hello " + text);
}
};
ihello.sayHello();
}
public static void main(String[] args) {
new HelloTest().hello();
}
}
public interface IHello {
void sayHello();
}以上代码定义了一个IHello接口,只包含一个抽象方法void sayHello(),这是使用匿名类的写法,编译器会提示你替换为 Lambda 表达式:
public class HelloTest {
private String text = " world";
private void hello() {
IHello ihello = () -> System.out.print("Hello " + text);
ihello.sayHello();
}
public static void main(String[] args) {
new HelloTest().hello();
}
}但如果IHello接口包含了两个或两个以上抽象方法,则编译器不会提示你替换为 Lambda 表达式,为什么?
因为,Java 中规定如果一个接口有且仅有一个抽象方法,那么接口就被称为函数接口/功能接口( Functional interface),例如Comparable , Runnable , EventListener , Comparator 等。在 Java8 及之后的版本中,函数接口可以使用 Lambda 表达式代替匿名类表达式,也就是说函数接口可以使用匿名函数代替匿名类实现,这就是 Lambda 表达式的特性之一。所以,了解匿名类有助于我们从本质上去了解匿名函数。
但是,为什么我们将这种接口称为函数接口呢?为啥不叫单方法接口(Single Method Interface)?
这是一个很好的问题,如果大家对函数式编程有所了解,就知道它可以传递代码,即函数,就像将数据或对象传递给方法一样。这些接口只有一种抽象方法被用于传递代码,就像函数式编程语言传递函数一样, 这就是为什么它们被称为函数接口。
一个简单的例子:
Runnable runnable = new Runnable(){
@Override
public void run(){
System.out.println("Running without Lambda");
}
};
new Thread(runnable).start();
// Running with Lambda
new Thread(() -> System.out.println("Running without Lambda")).start();仔细观察,我们正在使用这些接口将代码传递给 Thread 的构造函数 ,对于 Thread 类来说,重要的是 run 方法里的代码。而且,我们很容易替换 run 方法的实现,这些接口实际上是策略接口,因为这是策略模式的实现,其中,构成策略的代码被注入到在运行时运行该策略的代码中。
有且仅有一个抽象方法的接口当做函数一样使用,所以叫做函数接口,没毛病吧。那如果接口中声明多个抽象方法呢?还能这么用吗?答案是不能。因为你要实现所有的抽象方法,这时候 Lambda 表达式就会报错了。
函数接口是 Java 8 最重要的概念之一,为 Lambda 表达式提供了动力,但是许多开发人员没有首先了解函数接口在 Java 8 中的作用就花了很多精力来理解它,并花时间学习 Lambda 表达式和 Stream API . 除非您知道什么是功能接口以及Lambda与它之间的关系,否则您将无法使用 Java 8 的强大功能,例如 Lambda 表达式和流 API.
没有函数接口的知识,可能就无法理解在代码中可以使用 Lambda 的位置,并且很难编写所期望的 Lambda 表达式,因此,在 Java 8 中对函数接口有一个很好的了解是非常重要的。可以看到 Java 8 函数接口和 Lambda 表达式通过删除许多样板代码来帮助我们编写更小巧,更简洁的代码。
private void hello() {
//匿名内部类
IHello hello = new IHello() {
@Override
public void sayHello() {
System.out.print("Hello " + text);
}
};
//匿名函数(Lambda表达式)
IHello hello = () -> System.out.print("Hello " + text);
}匿名类和匿名函数的区别
| 匿名类 | 匿名函数( Lambda 表达式) |
|---|---|
| 没有名称的内部类 | 没有名称的函数 |
| 它可以实现包含任何抽象方法的接口 | 它可以实现一个包含单个抽象方法的接口(函数接口) |
| 它可以扩展抽象或具体的类 | 它不能扩展抽象或具体的类 |
| 匿名类可以具有实例变量和方法局部变量 | 匿名函数只能具有局部变量 |
在匿名内部类内部,this始终是指当前匿名类对象,而不是外部对象 | 在匿名函数内部,this始终引用当前的外部类对象 |
| 如果我们要处理多种方法,这是最佳选择 | 如果我们要处理接口,这是最佳选择 |
在编译时,将生成一个单独的.class文件 | 在编译时,不会生成单独的.class文件。只是将其转换为外部类的私有方法 |
| 每当我们创建对象时,内存分配都是按需的 | 它驻留在JVM的永久内存中 |
评论已关闭