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

posted @ 2023-06-15 14:04  黄河大道东  阅读(77)  评论(0编辑  收藏  举报