Anonymous Classes
匿名类使您可以使代码更简洁。它们使您可以同时声明和实例化一个类。它们就像本地类,只是它们没有名称。如果只需要使用一次本地类,则使用它们。
本节涵盖以下主题:
声明匿名类
本地类是类声明,而匿名类是表达式,这意味着您需要在另一个表达式中定义该类。下面的示例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
运算符 -
要实现的interface名称或要扩展的类的名称。在此示例中,匿名类正在实现interface
HelloWorld
。 -
括号中包含构造函数的参数,就像普通的类实例创建表达式一样。 注意 :实现interface时,没有构造函数,因此使用空括号对,如本例所示。
-
一个主体,它是一个类声明主体。更具体地说,在主体中,允许使用方法声明,但不允许使用语句。
因为匿名类定义是一个表达式,所以它必须是语句的一部分。在此示例中,匿名类表达式是实例化frenchGreeting
对象的语句的一部分。 (这解释了为什么在大括号后面有一个分号.)
访问封闭范围的局部变量,并声明和访问匿名类的成员
像本地类一样,匿名类也可以capture variables;它们对包含范围的局部变量具有相同的访问权:
-
匿名类可以访问其封闭类的成员。
-
匿名类无法在其封闭范围内访问未声明为
final
或实际上不是 final 的局部变量。 -
像嵌套类一样,匿名类中的类型声明(例如变量)会掩盖封闭范围内具有相同名称的任何其他声明。有关更多信息,请参见Shadowing。
匿名类在成员方面也具有与本地类相同的限制:
-
您不能在匿名类中声明静态初始化程序或成员interface。
-
匿名类可以具有静态成员,前提是它们是常量变量。
请注意,您可以在匿名类中声明以下内容:
-
Fields
-
额外的方法(即使它们未实现超类型的任何方法)
-
Instance initializers
-
Local classes
但是,您不能在匿名类中声明构造函数。
匿名类别的范例
匿名类通常在图形用户interface(GUI)应用程序中使用。
考虑一下 JavaFX 示例HelloWorld.java(来自JavaFX 入门的Hello World,JavaFX 风格部分)。此示例创建一个框架,其中包含一个“说'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
指定当您选择“说'Hello World'**”按钮时发生的情况。此方法需要类型为EventHandler<ActionEvent>
的对象。 EventHandler<ActionEvent>
interface仅包含一个方法,即 handle。该示例使用匿名类表达式来代替使用新类实现此方法。注意,此表达式是传递给btn.setOnAction
方法的参数。
由于EventHandler<ActionEvent>
interface仅包含一种方法,因此可以使用 lambda 表达式代替匿名类表达式。有关更多信息,请参见Lambda Expressions部分。
匿名类非常适合实现包含两个或多个方法的interface。以下 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("Text Field Sample");
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("Enter a numeric value");
}
@Override
public void replaceSelection(String text) {
if (!text.matches("[a-z, A-Z]")) {
super.replaceSelection(text);
}
}
};
sum.setPromptText("Enter the total");
sum.setPrefColumnCount(10);
GridPane.setConstraints(sum, 1, 0);
grid.getChildren().add(sum);
Button submit = new Button("Submit");
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);
}
}