匿名内部类与Lambda表达式不理解点
一、匿名内部类与Lambda表达式
// 传统匿名内部类实现 Runnable
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello from anonymous class!");
}
}).start();请你把语句拆分出来
代码思路
原始代码使用了传统的匿名内部类来实现 Runnable
接口并创建一个新线程。拆分的过程就是把匿名内部类提取出来,创建一个具体的类来实现 Runnable
接口,然后使用这个类的实例来创建线程。
拆分后的代码
// 定义一个具体的类,实现 Runnable 接口
class MyRunnable implements Runnable {
// 实现 Runnable 接口的 run 方法
@Override
public void run() {
// 线程要执行的任务,打印信息
System.out.println("Hello from anonymous class!");
}
}
public class Main {
public static void main(String[] args) {
// 创建 MyRunnable 类的实例
MyRunnable myRunnable = new MyRunnable();
// 使用 MyRunnable 实例创建一个新的线程
Thread thread = new Thread(myRunnable);
// 启动线程
thread.start();
}
}
代码解释
MyRunnable
类:
- 定义了一个名为
MyRunnable
的类,该类实现了Runnable
接口。- 实现了
Runnable
接口的run
方法,在该方法中打印了一条信息。
Main
类:
- 在
main
方法中,首先创建了MyRunnable
类的一个实例myRunnable
。- 然后使用
myRunnable
实例创建了一个新的Thread
对象thread
。- 最后调用
thread
的start
方法启动线程,线程启动后会执行MyRunnable
类的run
方法。
二、匿名内部类语法结构
1.基本概念
匿名内部类是没有名字的局部内部类,它可以扩展一个类或者实现一个接口。通常用于创建只使用一次的类实例。
// 继承一个类的匿名内部类
父类类型 变量名 = new 父类构造器(参数列表) {
// 匿名内部类的类体部分
// 可以重写父类的方法
@Override
public void 方法名() {
// 方法实现
}
};
// 实现一个接口的匿名内部类
接口类型 变量名 = new 接口名() {
// 匿名内部类的类体部分
// 必须实现接口的所有抽象方法
@Override
public void 方法名() {
// 方法实现
}
};
2.继承一个类的匿名内部类
// 定义一个父类
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
public class AnonymousClassExample {
public static void main(String[] args) {
// 使用匿名内部类继承 Animal 类
Animal dog = new Animal() {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
};
dog.makeSound();
}
}
⑴.关键点解析:
匿名内部类的本质:
new Animal() { ... }
并不是直接实例化父类Animal
,而是定义了一个继承Animal
的匿名子类。这个匿名子类重写了
makeSound()
方法,实现了"汪汪汪"的输出。多态的体现:
变量
dog
的编译时类型是Animal
(父类),但运行时类型是匿名子类。通过父类引用调用方法时,实际执行的是子类重写后的方法(动态绑定)。
⑵.完整写法
// 定义一个父类
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
// 定义一个子类继承 Animal 类
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
}
public class NormalClassExample {
public static void main(String[] args) {
// 创建 Dog 类的实例
Animal dog = new Dog();
dog.makeSound();
}
}
3.实现一个接口的匿名内部类
interface MyInterface {
void doSomething();
}
public class Main {
public static void main(String[] args) {
// 使用匿名内部类实现接口
MyInterface obj = new MyInterface() {
@Override
public void doSomething() {
System.out.println("Doing something...");
}
};
// 调用接口方法
obj.doSomething();
}
}
// 定义接口
interface MyInterface {
void doSomething();
}
// 定义一个具体的类来实现 MyInterface 接口
class MyInterfaceImpl implements MyInterface {
// 实现接口中的抽象方法
@Override
public void doSomething() {
System.out.println("Doing something...");
}
}
public class Main {
public static void main(String[] args) {
// 创建实现类的实例
MyInterface obj = new MyInterfaceImpl();
// 调用接口方法
obj.doSomething();
}
}
三、Lambda 表达式是什么?
Lambda 表达式是匿名函数的简写形式,它可以替代传统的匿名内部类,用于实现函数式接口(只有一个抽象方法的接口)。Lambda 的核心目的是:
简化代码:减少模板代码(如匿名类的冗余语法)。
支持函数式编程:将函数作为参数传递,或直接定义行为。
四、Lambda和匿名内部类的拆分理解
public class LambdaTest02 {
public static void main(String[] args) {
// 匿名内部类方式(匿名内部类可以是一个抽象类)
LambdaTest02.test(new Animal() {
@Override
public void run() {
System.out.println("Animal run....");
}
});
// 匿名内部类
LambdaTest02.doFly(new Flyable() {
@Override
public void run() {
System.out.println("run.....");
}
@Override
public void fly() {
System.out.println("fly.....");
}
});
}
public static void test(Animal a){
a.run();
}
public static void doFly(Flyable f){
f.fly();
f.run();
}
}
abstract class Animal{
public abstract void run();
}
interface Flyable {
void run();
void fly();
}
拆分:
1. 定义 Animal
抽象类
创建一个名为 Animal.java
的文件,内容如下:
// Animal.java
public abstract class Animal {
public abstract void run();
}
2. 定义 Flyable
接口
创建一个名为 Flyable.java
的文件,内容如下:
// Flyable.java
public interface Flyable {
void run();
void fly();
}
3. 创建继承 Animal
抽象类的具体类
创建一个名为 ConcreteAnimal.java
的文件,内容如下:
// ConcreteAnimal.java
public class ConcreteAnimal extends Animal {
@Override
public void run() {
System.out.println("Animal run....");
}
}
4. 创建实现 Flyable
接口的具体类
创建一个名为 ConcreteFlyable.java
的文件,内容如下:
// ConcreteFlyable.java
public class ConcreteFlyable implements Flyable {
@Override
public void run() {
System.out.println("run.....");
}
@Override
public void fly() {
System.out.println("fly.....");
}
}
5. 主类 LambdaTest02
创建一个名为 LambdaTest02.java
的文件,内容如下:
// LambdaTest02.java
public class LambdaTest02 {
public static void main(String[] args) {
// 创建 ConcreteAnimal 类的实例
ConcreteAnimal concreteAnimal = new ConcreteAnimal();
// 调用 test 方法
LambdaTest02.test(concreteAnimal);
// 创建 ConcreteFlyable 类的实例
ConcreteFlyable concreteFlyable = new ConcreteFlyable();
// 调用 doFly 方法
LambdaTest02.doFly(concreteFlyable);
}
public static void test(Animal a) {
a.run();
}
public static void doFly(Flyable f) {
f.fly();
f.run();
}
}
五、Lambda和匿名内部类的拆分理解
List<Integer> list = Arrays.asList(3, 6, 1, 7, 2, 5, 4);
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
System.out.println("排序后:" + list);
拆分
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
// 定义一个具体的类来实现 Comparator 接口
// 该类用于对 Integer 类型的元素进行降序排序
class IntegerDescComparator implements Comparator<Integer> {
// 实现 compare 方法,用于比较两个 Integer 对象
// 根据 o2 - o1 的结果来决定元素的顺序
// 如果结果大于 0,o2 排在 o1 前面;如果结果小于 0,o1 排在 o2 前面;如果结果等于 0,两者顺序不变
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
public class SortExample {
public static void main(String[] args) {
// 创建一个包含整数的列表
// Arrays.asList 方法用于将一组元素转换为 List 集合
List<Integer> list = Arrays.asList(3, 6, 1, 7, 2, 5, 4);
// 创建 IntegerDescComparator 类的实例
// 该实例将作为排序时的比较器
IntegerDescComparator comparator = new IntegerDescComparator();
// 使用 Collections.sort 方法对列表进行排序
// 传入列表和比较器作为参数,列表将按照比较器定义的规则进行排序
Collections.sort(list, comparator);
// 打印排序后的列表
System.out.println("排序后:" + list);
}
}
五、四个基本的函数式接口
名字 | 接口名 | 对应的抽象方法 |
---|---|---|
消费 | Consumer<T> | void accept(T t); |
生产 | Supplier<T> | T get(); |
转换 | Function<T, R> | R apply(T t); |
判断 | Predicate<T> | boolean test(T t); |
1.Iterable 接口的 forEach 方法
Iterable 接口是 Java 集合框架中的基础接口,Collection 接口继承自 Iterable 接口,因此像 List、Set 等集合类都可以使用 forEach 方法。
语法
default void forEach(Consumer<? super T> action)
这里的 action 是一个 Consumer 函数式接口的实例,它定义了对集合中每个元素要执行的操作。
import java.util.ArrayList;
import java.util.List;
public class IterableForEachExample {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");
// 使用 Lambda 表达式
fruits.forEach(fruit -> System.out.println(fruit));
// 使用方法引用
fruits.forEach(System.out::println);
}
}
在上述示例中,首先创建了一个 List 集合,然后分别使用 Lambda 表达式和方法引用两种方式调用 forEach 方法对集合中的每个元素进行打印操作。
2. Map 接口的 forEach 方法
Map 接口提供了 forEach 方法,用于遍历 Map 中的键值对。
default void forEach(BiConsumer<? super K,? super V> action)
这里的 action 是一个 BiConsumer 函数式接口的实例,它接受两个参数,分别是 Map 中的键和值,定义了对每个键值对要执行的操作。
import java.util.HashMap;
import java.util.Map;
public class MapForEachExample {
public static void main(String[] args) {
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 85);
scores.put("Bob", 90);
scores.put("Charlie", 78);
// 使用 Lambda 表达式
scores.forEach((name, score) -> System.out.println(name + ": " + score));
}
}
在这个示例中,创建了一个 Map 集合,然后使用 Lambda 表达式调用 forEach 方法,对 Map 中的每个键值对进行打印操作。
3.Consumer<T>
Consumer<T> 是一个消费型接口,它接受一个输入参数 T,并且不返回任何结果。其抽象方法是 void accept(T t)。
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class ConsumerExample {
public static void main(String[] args) {
// 创建一个包含整数的列表
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
// 使用 Consumer 打印列表中的每个元素
Consumer<Integer> printer = num -> System.out.println(num);
numbers.forEach(printer);
// 也可以直接在 forEach 中使用 Lambda 表达式
numbers.forEach(num -> System.out.println(num * 2));
}
}
在这个示例中,我们创建了一个 Consumer 实例 printer,它接受一个整数并将其打印出来。然后使用 forEach 方法遍历列表,并将 printer 作为参数传递给它。另外,我们还直接在 forEach 方法中使用 Lambda 表达式来打印每个元素的两倍。
⑴.不使用Lambda和匿名内部类
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
// 定义一个实现 Consumer 接口的类,用于打印整数
class IntegerPrinter implements Consumer<Integer> {
@Override
public void accept(Integer num) {
System.out.println(num);
}
}
// 定义一个实现 Consumer 接口的类,用于打印整数的两倍
class IntegerDoublePrinter implements Consumer<Integer> {
@Override
public void accept(Integer num) {
System.out.println(num * 2);
}
}
public class ConsumerExample {
public static void main(String[] args) {
// 创建一个包含整数的列表
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
// 创建 IntegerPrinter 类的实例
IntegerPrinter printer = new IntegerPrinter();
// 调用 forEach 方法,将 printer 作为参数传入,遍历列表并打印每个元素
numbers.forEach(printer);
// 创建 IntegerDoublePrinter 类的实例
IntegerDoublePrinter doublePrinter = new IntegerDoublePrinter();
// 调用 forEach 方法,将 doublePrinter 作为参数传入,遍历列表并打印每个元素的两倍
numbers.forEach(doublePrinter);
}
}
运行结果:
⑵.不使用 Lambda 表达式,使用匿名内部类
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class ConsumerExample {
public static void main(String[] args) {
// 创建一个包含整数的列表
List<Integer> numbers = new ArrayList<>();
// 向列表中添加元素 1
numbers.add(1);
// 向列表中添加元素 2
numbers.add(2);
// 向列表中添加元素 3
numbers.add(3);
// 向列表中添加元素 4
numbers.add(4);
// 创建一个实现了 Consumer 接口的匿名内部类实例,用于打印元素
Consumer<Integer> printer = new Consumer<Integer>() {
@Override
public void accept(Integer num) {
// 打印传入的整数
System.out.println(num);
}
};
// 调用 forEach 方法,将 printer 作为参数传入,遍历列表并打印每个元素
numbers.forEach(printer);
// 创建另一个实现了 Consumer 接口的匿名内部类实例,用于打印元素的两倍
Consumer<Integer> doublePrinter = new Consumer<Integer>() {
@Override
public void accept(Integer num) {
// 打印传入整数的两倍
System.out.println(num * 2);
}
};
// 调用 forEach 方法,将 doublePrinter 作为参数传入,遍历列表并打印每个元素的两倍
numbers.forEach(doublePrinter);
}
}
4.Supplier<T>
Supplier<T> 是一个供应型接口,它不接受任何参数,但返回一个结果 T。其抽象方法是 T get()。
import java.util.function.Supplier;
public class SupplierExample {
public static void main(String[] args) {
// 使用 Supplier 生成一个随机数
Supplier<Double> randomSupplier = () -> Math.random();
double randomNumber = randomSupplier.get();
System.out.println("随机数: " + randomNumber);
}
}
运行结果:
⑴.不使用Lambda和匿名内部类
import java.util.function.Supplier;
// 定义一个具体的类来实现 Supplier 接口
class RandomNumberSupplier implements Supplier<Double> {
// 实现 Supplier 接口的 get 方法
@Override
public Double get() {
return Math.random();
}
}
public class SupplierExample {
public static void main(String[] args) {
// 创建 RandomNumberSupplier 类的实例
RandomNumberSupplier randomSupplier = new RandomNumberSupplier();
// 调用 get 方法获取随机数
double randomNumber = randomSupplier.get();
// 打印生成的随机数
System.out.println("随机数: " + randomNumber);
}
}
⑵.不使用 Lambda 表达式,使用匿名内部类
import java.util.function.Supplier;
public class SupplierExample {
public static void main(String[] args) {
// 使用匿名内部类实现 Supplier 接口
Supplier<Double> randomSupplier = new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
};
double randomNumber = randomSupplier.get();
System.out.println("随机数: " + randomNumber);
}
}
5.Function<T, R>
Function<T, R> 是一个函数型接口,它接受一个输入参数 T,并返回一个结果 R。其抽象方法是 R apply(T t)。
import java.util.function.Function;
public class FunctionExample {
public static void main(String[] args) {
// 使用 Function 将字符串转换为整数
Function<String, Integer> stringToInt = str -> Integer.parseInt(str);
String input = "123";
int result = stringToInt.apply(input);
System.out.println("转换结果: " + result);
}
}
运行结果:
⑴.不使用Lambda和匿名内部类
// 导入 java.util.function 包中的 Function 接口,该接口是 Java 8 引入的函数式接口,用于表示接受一个参数并产生结果的函数
import java.util.function.Function;
// 定义一个具体的类 StringToIntFunction,该类实现了 Function 接口,指定泛型参数为 <String, Integer>
// 表示该函数接受一个 String 类型的参数,返回一个 Integer 类型的结果
class StringToIntFunction implements Function<String, Integer> {
// 重写 Function 接口的 apply 方法,该方法用于执行具体的函数逻辑
// 接收一个 String 类型的参数 str,并将其转换为 Integer 类型的值
@Override
public Integer apply(String str) {
// 使用 Integer 类的 parseInt 方法将字符串 str 转换为整数
return Integer.parseInt(str);
}
}
// 定义一个公共类 FunctionExample,包含程序的入口方法 main
public class FunctionExample {
// 程序的入口方法,Java 程序从这里开始执行
public static void main(String[] args) {
// 创建 StringToIntFunction 类的一个实例,用于后续的字符串到整数的转换操作
StringToIntFunction stringToInt = new StringToIntFunction();
// 定义一个字符串变量 input,初始值为 "123",该字符串将作为转换的输入
String input = "123";
// 调用 StringToIntFunction 实例的 apply 方法,将 input 字符串转换为整数
// 并将结果存储在 int 类型的变量 result 中
int result = stringToInt.apply(input);
// 使用 System.out.println 方法输出转换结果,输出格式为 "转换结果: " 加上转换后的整数
System.out.println("转换结果: " + result);
}
}
⑵.不使用 Lambda 表达式,使用匿名内部类
import java.util.function.Function;
public class FunctionExample {
public static void main(String[] args) {
// 使用匿名内部类实现 Function 接口
Function<String, Integer> stringToInt = new Function<String, Integer>() {
@Override
public Integer apply(String str) {
return Integer.parseInt(str);
}
};
String input = "123";
// 调用 apply 方法进行转换
int result = stringToInt.apply(input);
System.out.println("转换结果: " + result);
}
}
6.Predicate<T>
Predicate<T> 是一个断言型接口,它接受一个输入参数 T,并返回一个布尔值。其抽象方法是 boolean test(T t)。
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class PredicateExample {
public static void main(String[] args) {
// 创建一个包含整数的列表
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
// 使用 Predicate 过滤出偶数
Predicate<Integer> isEven = num -> num % 2 == 0;
for (Integer num : numbers) {
if (isEven.test(num)) {
System.out.println(num);
}
}
}
}
运行结果:
⑴.不使用Lambda和匿名内部类
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
// 定义一个具体的类实现 Predicate 接口
class EvenNumberPredicate implements Predicate<Integer> {
// 实现 test 方法,判断是否为偶数
@Override
public boolean test(Integer num) {
return num % 2 == 0;
}
}
public class PredicateExample {
public static void main(String[] args) {
// 创建一个包含整数的列表
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
// 创建 EvenNumberPredicate 类的实例
EvenNumberPredicate isEven = new EvenNumberPredicate();
// 遍历列表,使用 Predicate 过滤出偶数
for (Integer num : numbers) {
if (isEven.test(num)) {
System.out.println(num);
}
}
}
}
⑵.不使用 Lambda 表达式,使用匿名内部类
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class PredicateExample {
public static void main(String[] args) {
// 创建一个包含整数的列表
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
// 使用匿名内部类实现 Predicate 接口
Predicate<Integer> isEven = new Predicate<Integer>() {
// 实现 test 方法,判断是否为偶数
@Override
public boolean test(Integer num) {
return num % 2 == 0;
}
};
// 遍历列表,使用 Predicate 过滤出偶数
for (Integer num : numbers) {
if (isEven.test(num)) {
System.out.println(num);
}
}
}
}