局部类的定义
局部类(Local Classes)是定义在任何代码块中的类,比如定义在方法中,for或者if语句里。
下面代码在方法validatePhoneNumber中定义了PhoneNumber类:
public class LocalClassExample {
static String regularExpression = "[^0-9]";
public static void validatePhoneNumber(
String phoneNumber1, String phoneNumber2) {
final int numberLength = 10;
// Valid in JDK 8 and later:
// int numberLength = 10;
class PhoneNumber {
String formattedPhoneNumber = null;
PhoneNumber(String phoneNumber){
// numberLength = 7;
String currentNumber = phoneNumber.replaceAll(
regularExpression, "");
if (currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}
public String getNumber() {
return formattedPhoneNumber;
}
// Valid in JDK 8 and later:
// public void printOriginalNumbers() {
// System.out.println("Original numbers are " + phoneNumber1 +
// " and " + phoneNumber2);
// }
}
PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
// Valid in JDK 8 and later:
// myNumber1.printOriginalNumbers();
if (myNumber1.getNumber() == null)
System.out.println("First number is invalid");
else
System.out.println("First number is " + myNumber1.getNumber());
if (myNumber2.getNumber() == null)
System.out.println("Second number is invalid");
else
System.out.println("Second number is " + myNumber2.getNumber());
}
public static void main(String... args) {
validatePhoneNumber("123-456-7890", "456-7890");
}
}
以上代码主要功能是验证格式化后的电话号码是否由10个数字组成的,输出结果:
First number is 1234567890
Second number is invalid
访问外部类的成员
A local class has access to the members of its enclosing class. In the previous example, the PhoneNumber constructor accesses the member LocalClassExample.regularExpression.
局部类可以访问外部类的成员,例如上面例子中的LocalClassExample.regularExpression
。
另外,局部类可以访问局部变量,但是必须是final
的局部变量。
不过,Java SE8之后,局部类可以访问final
或者是effectively final
的局部变量。effectively final
的定义是某个变量初始化后没有发生改变。如下所示,假如numberLength
在前面不是定义成final的,然后在
赋成7:
PhoneNumber(String phoneNumber) {
numberLength = 7;
String currentNumber = phoneNumber.replaceAll(
regularExpression, "");
if (currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}
因为重新赋值了,numberLength
不再是effectively final
的。如果内部类PhoneNumber
试图访问它,会产生编译错误(local variables referenced from an inner class must be final or effectively final):
if (currentNumber.length() == numberLength)
Java SE8之后,如果局部类定义在方法中,他可以访问方法参数:
public void printOriginalNumbers() {
System.out.println("Original numbers are " + phoneNumber1 +
" and " + phoneNumber2);
}
Shadowing and Local Classes
Declarations of a type (such as a variable) in a local class shadow declarations in the enclosing scope that have the same name. See Shadowing for more information.
投影
局部类中变量的定义会将所在作用域相同名称的变量投影,即不能直接访问外部同名变量。
类似于内部类
类似于内部类 (inner classes),局部类也不能声明或定义任何静态成员。静态方法中的局部类,比如PhoneNumber
,定义在了静态方法validatePhoneNumber
中,因此只能访问外部类的静态成员。比如,如果将regularExpression 定义成了非静态的,会产生编译错误(non-static variable regularExpression cannot be referenced from a static context.)
局部类中不能包含大部分静态声明,除非它是常量。
不能在块中定义接口,接口本来就是静态的。比如,以下代码不能通过编译,因为HelloThere
定义在了greetInEnglish
中。
public void greetInEnglish() {
interface HelloThere {
public void greet();
}
class EnglishHelloThere implements HelloThere {
public void greet() {
System.out.println("Hello " + name);
}
}
HelloThere myGreeting = new EnglishHelloThere();
myGreeting.greet();
}
局部类中不能定义静态初始化块和接口。如下,EnglishGoodbye.sayGoodbye
是静态的,而他定义在了局部类中,编译将不通过。
不过,静态成员如果是常量,又可以包含在局部类中。(常量指8种基本类型char,short,byte,int,long,float,double,boolean
和String
定义成final的,并且所赋的值在编译期可以确定,即算数表达式或字符串)。以下代码编译可以通过,因为静态成员EnglishGoodbye.farewell
是常量。
public void sayGoodbyeInEnglish() {
class EnglishGoodbye {
public static final String farewell = "Bye bye";
public void sayGoodbye() {
System.out.println(farewell);
}
}
EnglishGoodbye myEnglishGoodbye = new EnglishGoodbye();
myEnglishGoodbye.sayGoodbye();
}