Java教程是为JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
有关Java SE 9及后续版本中更新的语言特性的摘要,请参阅Java语言更改。
有关所有JDK版本的新功能、增强功能和已删除或不推荐使用的选项的信息,请参阅JDK发布说明。
匿名类使代码更简洁。它们允许你在声明和实例化类的同时进行。它们类似于局部类,但没有名称。如果你只需要使用局部类一次,就可以使用它们。
本节涵盖以下主题:
局部类是类声明,而匿名类是表达式,这意味着你在另一个表达式中定义类。下面的示例HelloWorldAnonymousClasses
在局部变量frenchGreeting
和spanishGreeting
的初始化语句中使用了匿名类,但是在变量englishGreeting
的初始化中使用了局部类::
public class HelloWorldAnonymousClasses { interface HelloWorld { public void greet(); public void greetSomeone(String someone); } public void sayHello() { class EnglishGreeting implements HelloWorld { String name = "world"; public void greet() { greetSomeone("world"); } public void greetSomeone(String someone) { name = someone; System.out.println("Hello " + name); } } HelloWorld englishGreeting = new EnglishGreeting(); HelloWorld frenchGreeting = new HelloWorld() { String name = "tout le monde"; public void greet() { greetSomeone("tout le monde"); } public void greetSomeone(String someone) { name = someone; System.out.println("Salut " + name); } }; HelloWorld spanishGreeting = new HelloWorld() { String name = "mundo"; public void greet() { greetSomeone("mundo"); } public void greetSomeone(String someone) { name = someone; System.out.println("Hola, " + name); } }; englishGreeting.greet(); frenchGreeting.greetSomeone("Fred"); spanishGreeting.greet(); } public static void main(String... args) { HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses(); myApp.sayHello(); } }
如前所述,匿名类是一个表达式。匿名类表达式的语法类似于调用构造函数,只是在代码块中包含一个类定义。
考虑实例化 frenchGreeting
对象的情况:
HelloWorld frenchGreeting = new HelloWorld() { String name = "tout le monde"; public void greet() { greetSomeone("tout le monde"); } public void greetSomeone(String someone) { name = someone; System.out.println("Salut " + name); } };
匿名类表达式由以下部分组成:
new
运算符
要实现的接口的名称或要扩展的类的名称。在这个例子中,匿名类正在实现接口 HelloWorld
。
括号中包含构造函数的参数,就像正常的类实例创建表达式一样。注意:当你实现一个接口时,没有构造函数,所以你使用一个空的括号对,就像这个例子中一样。
一个类声明体的主体。更具体地说,在主体中,允许方法声明,但不允许语句。
由于匿名类定义是一个表达式,它必须是语句的一部分。在这个例子中,匿名类表达式是实例化 frenchGreeting
对象的语句的一部分。(这就解释了为什么在右括号之后有一个分号。)
与局部类一样,匿名类可以捕获变量;它们可以访问封闭作用域的局部变量:
匿名类可以访问其封闭类的成员。
匿名类不能访问封闭作用域中未声明为 final
或有效 final 的局部变量。
与嵌套类一样,匿名类中对类型(如变量)的声明会隐藏封闭作用域中具有相同名称的其他声明。有关更多信息,请参见Shadowing。
匿名类对于其成员也有与局部类相同的限制:
你不能在匿名类中声明静态初始化器或成员接口。
匿名类可以有静态成员,但必须是常量变量。
请注意,您可以在匿名类中声明以下内容:
字段
额外的方法(即使它们不实现任何超类型的方法)
实例初始化器
局部类
但是,您不能在匿名类中声明构造函数。
匿名类通常在图形用户界面(GUI)应用程序中使用。
考虑JavaFX示例 HelloWorld.java
(来自 Hello World, JavaFX Style一节,来自 JavaFX入门指南)。此示例创建一个包含一个Say 'Hello World'按钮的框架。匿名类表达式已突出显示:
import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.StackPane; import javafx.stage.Stage; public class HelloWorld extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Hello World!"); Button btn = new Button(); btn.setText("Say 'Hello World'"); btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { System.out.println("Hello World!"); } }); StackPane root = new StackPane(); root.getChildren().add(btn); primaryStage.setScene(new Scene(root, 300, 250)); primaryStage.show(); } }
在此示例中,方法调用btn.setOnAction
指定选择Say 'Hello World'按钮时发生的情况。此方法需要一个EventHandler<ActionEvent>
类型的对象。 EventHandler<ActionEvent>
接口只包含一个方法handle。示例中使用匿名类表达式而不是使用新类实现此方法。请注意,此表达式是传递给btn.setOnAction
方法的参数。
由于EventHandler<ActionEvent>
接口只包含一个方法,因此可以使用lambda表达式而不是匿名类表达式。有关更多信息,请参见Lambda表达式一节。
匿名类非常适合实现包含两个或多个方法的接口。下面的JavaFX示例来自自定义UI控件一节。突出显示的代码创建一个只接受数值的文本字段。它通过使用匿名类重写继承自TextInputControl
类的replaceText
和replaceSelection
方法来重新定义TextField
类的默认实现。
import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.stage.Stage; public class CustomTextFieldSample extends Application { final static Label label = new Label(); @Override public void start(Stage stage) { Group root = new Group(); Scene scene = new Scene(root, 300, 150); stage.setScene(scene); stage.setTitle("文本框示例"); GridPane grid = new GridPane(); grid.setPadding(new Insets(10, 10, 10, 10)); grid.setVgap(5); grid.setHgap(5); scene.setRoot(grid); final Label dollar = new Label("$"); GridPane.setConstraints(dollar, 0, 0); grid.getChildren().add(dollar); final TextField sum = new TextField() { @Override public void replaceText(int start, int end, String text) { if (!text.matches("[a-z, A-Z]")) { super.replaceText(start, end, text); } label.setText("请输入数值"); } @Override public void replaceSelection(String text) { if (!text.matches("[a-z, A-Z]")) { super.replaceSelection(text); } } }; sum.setPromptText("输入总数"); sum.setPrefColumnCount(10); GridPane.setConstraints(sum, 1, 0); grid.getChildren().add(sum); Button submit = new Button("提交"); GridPane.setConstraints(submit, 2, 0); grid.getChildren().add(submit); submit.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { label.setText(null); } }); GridPane.setConstraints(label, 0, 1); GridPane.setColumnSpan(label, 3); grid.getChildren().add(label); scene.setRoot(grid); stage.show(); } public static void main(String[] args) { launch(args); } }