原文地址:https://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html
一Inner Class
public class Test2 {
private final static int SIZE = 15;
private int[] arr = new int[SIZE];
public Test2() {
// fill the array with ascending integer values
for (int i = 0; i < SIZE; i++) {
arr[i] = i;
}
}
public void printEven() {
EvenIterator iterator = this.new EvenIterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println();
}
private class EvenIterator implements Iterator<Integer> {
private int nextIndex = 0;
@Override
public boolean hasNext() {
return (nextIndex <= SIZE - 1);
}
@Override
public Integer next() {
Integer retValue = Integer.valueOf(arr[nextIndex]);
nextIndex += 2;
return retValue;
}
}
public static void main(String[] args) {
Test2 t=new Test2();
t.printEven();
}
}
1内部类可以直接使用外部类的实例变量
2局部类和匿名类是内部类的另外两种形式
局部类 在方法体里的定义的内部类
匿名类 在方体体里定义且没有显示命名的内部类
3modifiers同外部类
二Local Classes
局部类可定义在任何表达式,语句,代码块中
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");
}
}
1局部类可以使用所在类的成员变量
2局部类访问其所在代码块范围内声明为final的局部变量,从JDK8起,还可以是effectively
final 的局部变量
3从1.8开始局部类还可以获取到它所在方法的形参
4Shadowing 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.
5局部类是非静态的,可以初始化所在类的成员变量,因此不能包含大多数静态声明
(1)局部类不能定义或声明静态成员变量
(2)在代码块中不能声明接口,因为接口里是static的;
❎
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();
}
在局部类中不能定义静态构造器或成员接口;方法
❎
public void sayGoodbyeInEnglish() {
class EnglishGoodbye {
public static void sayGoodbye() {
System.out.println("Bye bye");
}
}
EnglishGoodbye.sayGoodbye();
}
(3)局部类中可以有静态常量
✅
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();
}
三Anonymous Classes
类似内部类但没有名称,若只使用一次内部类可以用匿名类
public class Test2 {
interface HelloWord{
public void greet();
public void greetSomeone(String someone);
}
public void sayHello(){
class EnglishGreeting implements HelloWord{
String name="world";
public void greet(){
greetSomeone("world");
}
public void greetSomeone(String someone){
name=someone;
System.out.println("Hello "+name);
}
}
HelloWord enlishGreeting=new EnglishGreeting();
HelloWord frenchGreeting=new HelloWord() {
String name="123";
@Override
public void greet() {
greetSomeone("tout le monde");
}
@Override
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
enlishGreeting.greet();
frenchGreeting.greetSomeone("Fred");
}
public static void main(String[] args) {
Test2 test=new Test2();
test.sayHello();
}
}
语法:
匿名类是一个表达式,匿名类的语法类似调用构造器创建对象,只是增加了定义类的代码块
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);
}
};
匿名类表达式由下组成:
(1)句柄
(2)要实现的接口或要继承的类的名字,如例子中HelloWorld
(3)包含构造函数的参数,就像普通的类实例创建表达式一样。
(4)定义类的代码块
Because an anonymous class definition is an expression, it must be part of a statement. In this example, the anonymous class expression is part of the statement that instantiates the frenchGreeting
object. (This explains why there is a semicolon after the closing brace.)
作用域:
1匿名类可以使用外层类的成员变量
2局部类不能访问它所在代码块中定义的非final或effectively final修饰的局部变量
3 Like a nested class, a declaration of a type (such as a variable) in an anonymous class shadows any other declarations in the enclosing scope that have the same name. See Shadowing for more information.
匿名类对于成员的限制与局部类类似
-
You cannot declare static initializers or member interfaces in an anonymous class.
-
An anonymous class can have static members provided that they are constant variables.
Note that you can declare the following in anonymous classes:
-
Fields
-
Extra methods (even if they do not implement any methods of the supertype)
-
Instance initializers
-
Local classes
However, you cannot declare constructors in an anonymous class.
四 Lambda Expressions
import lombok.Data;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@Data
public class Person {
public enum Sex {
MALE, FEMALE
}
String name;
LocalDate birthday;
Sex gender;
String emailAddress;
Integer age;
public void printPersion() {
System.out.println(name + " " + age);
}
public static List<Person> createRoster() {
List<Person> list = new ArrayList<>();
Person person1 = new Person();
person1.setName("xiaoming");
person1.setAge(10);
Person person2 = new Person();
person2.setName("xiaohong");
person2.setAge(20);
list.add(person1);
list.add(person2);
return list;
}
}
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class RosterTest {
interface CheckPerson {
boolean test(Person p);
}
//Approach 1
public static void printPersonOlderThan(List<Person> roster, int age) {
for (Person p : roster) {
if (p.getAge() >= age) {
p.printPersion();
}
}
}
//Approach 2
public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high) {
for (Person p : roster) {
if (low <= p.getAge() && p.getAge() < high) {
p.printPersion();
}
}
}
//Approach 3,4,5
public static void printPersons(List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPersion();
}
}
}
//Approach 6 Use standard functional interfaces with lamda expressions
public static void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPersion();
}
}
}
//Approach 7:
public static void processPersons(List<Person> roster, Predicate<Person> tester, Consumer<Person> block) {
for (Person p : roster) {
if (tester.test(p)) {
block.accept(p);
}
}
}
public static void processPersonsWithFunction(List<Person> roster, Predicate<Person> tester, Function<Person, String> mapper, Consumer<String> block) {
for (Person p : roster) {
if (tester.test(p)) {
String data = mapper.apply(p);
block.accept(data);
}
}
}
public static <X, Y> void processElements(Iterable<X> source, Predicate<X> tester, Function<X, Y> mapper, Consumer<Y> block) {
for (X p : source) {
if (tester.test(p)) {
Y data = mapper.apply(p);
block.accept(data);
}
}
}
public static void main(String[] args) {
List<Person> roster = Person.createRoster();
// Approach 1: Create Methods that Search for Persons that Match One
printPersonOlderThan(roster, 10);
// Approach 2: Create More Generalized Search Methods
printPersonsWithinAgeRange(roster, 14, 30);
// Approach 3: Specify Search Criteria Code in a Local Class
class CheckPersonEligibleForSelectiveService implements CheckPerson {
@Override
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
printPersons(roster, new CheckPersonEligibleForSelectiveService());
// Approach 4: Specify Search Criteria Code in an Anonymous Class
printPersons(roster, new CheckPerson() {
@Override
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
});
// Approach 5:考虑到CheckPerson接口只包含一个方法,匿名类的语法很笨重,这种情况下可使用lamda表达式来代替匿名类
//CheckPerson接口是一个函数式接口。函数式接口只包含一个抽象方法(可以包含一个或多个default methods或静态方法)。
//因此实现函数式接口时可以省略抽象方法名字
// Specify Search Criteria Code with a Lambda Expression
printPersons(roster, (Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
// Approach 6: Use Standard Functional Interfaces with Lambda
//CheckPerson函数式接口包含的方法,有一个输入参数,返回一个布尔类型。JDK定义了几个标准函数式接口(package java.util.function)
//以使你不需要自己定义这种简单接口。例如:可以使用Predicate<T> 接口替代CheckPerson
printPersonsWithPredicate(roster, p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25);
// Approach 7: Use Lamba Expressions Throughout Your Application
//Consumer<T> block :void accept(T t);
processPersons(roster,p->p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25,p->p.printPersion());
// Approach 7, second example
//Function<T,R> :R apply(T t);
processPersonsWithFunction(roster, p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25,
p -> p.getEmailAddress(), email -> System.out.println(email));
// Approach 8: Use Generics More Extensively
processElements(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25,
p -> p.getEmailAddress(),
email -> System.out.println(email)
);
// Approach 9: Use Bulk Data Operations That Accept Lambda Expressions
// processElements Action Aggregate Operation
// Obtain a source of objects Stream<E> stream()
// Filter objects that match a Predicate object Stream<T> filter(Predicate<? super T> predicate)
// Map objects to another value as specified by a Function object <R> Stream<R> map(Function<? super T,? extends R> mapper)
// Perform an action as specified by a Consumer object void forEach(Consumer<? super T> action)
roster.stream().filter( p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25).map(p->p.getEmailAddress()).forEach(email-> System.out.println(email));
}
}
- 语法:
1
Note: You can omit the data type of the parameters in a lambda expression. In addition, you can omit the parentheses if there is only one parameter. For example, the following lambda expression is also valid:
2 The arrow token, ->
3
A body, which consists of a single expression or a statement block. This example uses the following expression:
p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25
If you specify a single expression, then the Java runtime evaluates the expression and then returns its value. Alternatively, you can use a return statement:
p -> { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; }
A return statement is not an expression; in a lambda expression, you must enclose statements in braces ({}
). However, you do not have to enclose a void method invocation in braces. For example, the following is a valid lambda expression:
email -> System.out.println(email)
- 注:可以把lamda表达式看作使是匿名方法(没有名字的方法)
- 接受一个以上形式参数的lambda表达式:
public class Calculator { interface IntegerMath { int operation(int a, int b); } public int operateBinary(int a, int b, IntegerMath op) { return op.operation(a, b); } public static void main(String... args) { Calculator myApp = new Calculator(); IntegerMath addition = (a, b) -> a + b; IntegerMath subtraction = (a, b) -> a - b; System.out.println("40 + 2 = " + myApp.operateBinary(40, 2, addition)); System.out.println("20 - 10 = " + myApp.operateBinary(20, 10, subtraction)); } }
- 访问包围范围内的局部变量 没懂??
??????Like local and anonymous classes, lambda expressions can capture variables; they have the same access to local variables of the enclosing scope. However, unlike local and anonymous classes, lambda expressions do not have any shadowing issues (see Shadowing for more information). Lambda expressions are lexically scoped. This means that they do not inherit any names from a supertype or introduce a new level of scoping. Declarations in a lambda expression are interpreted just as they are in the enclosing environment. T
import java.util.function.Consumer; public class LambdaScopeTest { public int x = 0; class FirstLevel { public int x = 1; void methodInFirstLevel(int x) { int z = 2; Consumer<Integer> myConsumer = (y) -> { // The following statement causes the compiler to generate // the error "Local variable z defined in an enclosing scope // must be final or effectively final" // // z = 99; System.out.println("x = " + x); System.out.println("y = " + y); System.out.println("z = " + z); System.out.println("this.x = " + this.x); System.out.println("LambdaScopeTest.this.x = " + LambdaScopeTest.this.x); }; myConsumer.accept(x); } } public static void main(String... args) { LambdaScopeTest st = new LambdaScopeTest(); LambdaScopeTest.FirstLevel fl = st.new FirstLevel(); fl.methodInFirstLevel(23); } }
This example generates the following output:
x = 23 y = 23 z = 2 this.x = 1 LambdaScopeTest.this.x = 0
If you substitute the parameter x
in place of y
in the declaration of the lambda expression myConsumer
, then the compiler generates an error:
Consumer<Integer> myConsumer = (x) -> { // ... }
The compiler generates the error "Lambda expression's parameter x cannot redeclare another local variable defined in an enclosing scope" because the lambda expression does not introduce a new level of scoping. Consequently, you can directly access fields, methods, and local variables of the enclosing scope. For example, the lambda expression directly accesses the parameter x
of the method methodInFirstLevel
. To access variables in the enclosing class, use the keyword this
. In this example, this.x
refers to the member variable FirstLevel.x
.
However, like local and anonymous classes, a lambda expression can only access local variables and parameters of the enclosing block that are final or effectively final. In this example, the variable z
is effectively final; its value is never changed after it's initialized. However, suppose that you add the following assignment statement in the the lambda expression myConsumer
:
Consumer<Integer> myConsumer = (y) -> { z = 99; // ... }
Because of this assignment statement, the variable z
is not effectively final anymore. As a result, the Java compiler generates an error message similar to "Local variable z defined in an enclosing scope must be final or effectively final".
关联阅读:
Lesson: Aggregate Operations
Lesson: Aggregate Operations (The Java™ Tutorials > Collections) (oracle.com)