JAVA匿名内部类详细解读
一、匿名内部类的定义
- 最本质的还是一个类
- 是一个内部类(有关内部类的内容不在这里讲述了)
- 该类没有具体名字(但是系统会分配一个代号在内存中)
匿名内部类声名格式:
public class T {
public static void main(String[] args) {
new Date() { // Date可以是任何没有被final关键字修改的已知类或接口
// 匿名内部类中的逻辑
// ...
};
}
}
二、匿名内部类的应用场景
假设现在有一个接口A,内部有一个未被实现的方法eat,如果想在main中直接调用eat方法,则按照传统思路需要一个类B来实现接口A,同时再创建类B的对象来调用A,如下
public class T {
public static void main(String[] args) {
B b = new B();
b.eat();
}
}
interface A{
public void eat();
}
class B implements A{
@Override
public void eat() {
System.out.println("正在调用eat方法");
}
}
----------------------
正在调用eat方法
下面这种写法就可以很好的简化代码的书写,此时的匿名内部类相当于一个对象,它的后面可以直接调用eat方法,非常的简便快捷,当A里面有多个方法时,如果想要同时调用,可以采用下面的写法:
而此时并没有显式的
声名这个接口就直接实现了里面的方法,就称之为匿名内部类
,其实这个类是被临时创建了,在内存中存在系统设定的名字,可以使用getClass()
方法来得到匿名内部类的(名字)
public class T {
public static void main(String[] args) {
A a = new A(){
@Override
public void eat() {
System.out.println("正在调用eat方法");
}
public void drink(){
System.out.println("正在调用drink方法");
}
};
a.eat();
a.drink();
// 匿名内部类的名字为 T$1 ,当下一个匿名内部类时,就会变成$2,以此类推
System.out.println(a.getClass()); // 获取类名
}
}
interface A{
void eat();
void drink();
}
-------------------
正在调用eat方法
正在调用drink方法
class org.ailun.T$1
三、匿名内部类的最常使用场景
通过实参的形式来使用,大大简化代码的书写
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.function.BinaryOperator;
/**
* @author JHL
* @version 1.0
* @date 2023/6/15 13:16
* @since : JDK 11
*/
public class T {
public static void main(String[] args) {
test1();
test2();
test3();
test4();
test5();
}
private static void test1() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("######################### \t[ 匿名内部类使用场景之:多线程使用场景 ]\t #########################");
}
}).start();
}
private static void test2() {
List<Integer> l = Arrays.asList(1, 2, 3, 4, 5, 6);
Integer value = l.stream().reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer i1, Integer i2) {
return i1 + i2;
}
}).get();
System.out.println("######################### \t[ 匿名内部类使用场景之:lambda表达式 ]\t #########################");
System.out.println(value);
}
private static void test3() {
Date d = new Date() {
private String a = "动态添加实例变量";
private static final String B = "动态添加类变量";
public void print() {
System.out.println("######################### \t[ 匿名内部类使用场景之:调用动态添加的方法 ]\t #########################");
System.out.println(a);
System.out.println(B);
}
@Override
public String toString() {
System.out.println("######################### \t[ 匿名内部类使用场景之:重写父类方法 ]\t #########################");
System.out.println(this.getClass());
return super.toString();
}
};
System.out.println(d);
try {
Method m = d.getClass().getDeclaredMethod("print");
m.invoke(d);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void test4() {
try {
System.out.println("######################### \t[ 匿名内部类使用场景之:获取声名侧泛型参数类型 ]\t #########################");
ArrayList l = get(new TypeReference<>() {
});
System.out.println("######################### \t[ l = " + l + " ]\t #########################");
Date d = get(new TypeReference<>() {
});
System.out.println("######################### \t[ d = " + d + " ]\t #########################");
} catch (Exception e) {
e.printStackTrace();
}
}
private static void test5() {
f(new A() {
@Override
public void eat() {
System.out.println("######################### \t[ 匿名内部类使用场景之:没有显式的实现A类,就成功调用f方法 ]\t #########################");
}
});
}
public static void f(A a) {
a.eat();
}
public static <T> T get(TypeReference<T> typeReference) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Type type = typeReference.getType();
Class clazz = (Class) type;
return (T) clazz.getDeclaredConstructor().newInstance();
}
}
interface A {
void eat();
}
abstract class TypeReference<T> {
private final Type type;
public TypeReference() {
Type superClass = getClass().getGenericSuperclass();
if (!(superClass instanceof ParameterizedType)) {
throw new IllegalArgumentException("无泛型类型信息");
}
type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
public Type getType() {
return type;
}
}
四、JAVA匿名内部类的注意事项
- 使用匿名内部类时,必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口
- 匿名内部类中不能定义构造函数
- 匿名内部类中不能定义静态方法
- 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效
- 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法
- 被
final
关键字修饰过的已知类不能有匿名内部类,被final
关键字修饰过的方法,在匿名内部类中不能重写
参考文章:https://blog.csdn.net/xueyukun1/article/details/121412738