java 访问内部匿名类成员

c3frrgcw  于 2023-09-29  发布在  Java
关注(0)|答案(9)|浏览(112)

除了使用反射之外,还有其他方法可以访问匿名内部类的成员吗?

8ulbf1ek

8ulbf1ek1#

匿名内部类有类型但没有名称。
可以访问未由命名超类型定义的字段。但是,一旦分配给命名类型变量,接口就会丢失。
显然,您可以从内部类本身访问这些字段。添加代码的一种方法是通过示例初始化器:

final AtomicInteger y = new AtomicInteger();
new Runnable() {
    int x;
    {
        x = 5;
        doRun(this);
        y.set(x);
    }
    public void run() {
        ... blah ...
    }
};

由匿名内部类表达式返回的值具有匿名类型,因此您有一次机会在类本身之外使用它:

final int y = new Runnable() {
    int x;
    {
        x = 5;
        doRun(this);
    }
    public void run() {
        ... blah ...
    }
}.x;

你也可以通过一个方法来传递它,该方法的声明类似于:

<T extends Runnable> T doRun(T runnable);
lpwwtiir

lpwwtiir2#

可以使用本地类代替匿名类。你看:

public class Test {
    public static void main(String... args) {
        class MyInner {
            private int value = 10;
        }

        MyInner inner = new MyInner();
        System.out.println(inner.value);
    }
}

但是,只能在方法体中引用MyInner类型。因此,在方法之外,您将无法使用未在其超类(本例中为java.lang.Object)或接口中声明的字段/方法。

qni6mghb

qni6mghb3#

public class AccessAnonymous {
    private Runnable runnable; // to have instance of the class

    public static void main(String[] args) throws Exception {
        AccessAnonymous a = new AccessAnonymous();
        a.a(); // init field

        Class clazz = a.runnable.getClass();
        Field field = clazz.getDeclaredField("i");
        field.setAccessible(true);

        int int1 = field.getInt(a.runnable);
        System.out.println("int1=" + int1);
    }

    public void a() {
        runnable = new Runnable() {
            private int i = 1;

            public void run() {
                i = 90;
            }

        };
        runnable.run();// change value
    }
}
m1m5dgzv

m1m5dgzv4#

在匿名类的情况下,在类引起的混乱和匿名的便利性之间也有一个折衷。复杂的类很少属于匿名类,而是命名的私有内部类。
在大多数匿名类中,我们只需要“喂养”知识,并且可以在构建时完成。在一些匿名类(例如,返回值载体)中,我们也关心一个返回值。
正如我们所知,数据成员不应该被直接访问,而应该使用getter setter。如果你发现自己在添加了很多getter和setter的情况下,你可能做错了什么,不应该使用匿名类。

lskq00tm

lskq00tm5#

如果它实现了一个接口或扩展了一个现有类,则可以访问在接口或基类中定义的成员。

j0pj023g

j0pj023g6#

Fooz先生是对的,除了接口只能定义常量成员。最好的方法是在接口中添加getter/setter方法,然后使用它们来获取值。但是对于每个匿名类,你必须定义那些方法(有点痛苦)。

a5g8bdjr

a5g8bdjr7#

如果你想要可读的、可维护的代码,不要使用匿名类。如果你使用匿名类,并且想要可读的、可维护的代码,那么当你需要访问内部类中的元素时,不要使用匿名类。有很多方法可以做到这一点,但我请求你不要使用任何这些黑客。可读性胜过所有其他优点。

s8vozzvw

s8vozzvw8#

这个问题已经有15年的历史了,但我认为还有另一种方法可以做到这一点!
该示例涉及为MyService编写单元测试:

public class MyService {

    public Result doSomething() {
        Result result = new Result();
        result.setToday(today();
        return result;
    }

    // not private but package-private so it can be overridden
    Date today() {
        return new Date();
    }
}

下面是单元测试:

public class MyServiceTest {

    private Date capturedToday;

    // anonymous inner class
    private final MyService testSubject = new MyService() {

        @Override
        Date today() {
            // capture the Date instance created by MyService
            Date today = super.today();
            MyServiceTest.this.capturedToday = today;
            return today;
        }
    };

    @Test
    void myTest() {

        MyResult result = testSubject.doSomething();            
        assertSame(capturedToday, result.getToday());

在本例中,MyService在内部创建了一个Date,并将其设置在结果上。为了检查这个逻辑,我创建了一个匿名内部类,它在创建示例时捕获示例并将其存储在capturedToday字段中:

MyServiceTest.this.capturedToday = today;

此语法允许内部类访问其外部类的字段,从而公开其值。

ubof19bj

ubof19bj9#

如果使用var,则可以从声明了匿名类的块(或子块)内部访问匿名类的成员。范例:

public static void main(String[] args) {
    var annon = new Object() {
        int field;
        void method() {
            System.out.println("method called with field=" + field);
        }
    };

    annon.field = 123;
    annon.method();  // prints "method called with field=123"
}

如果成员声明为static,这也有效。
如果我们声明了Object annon = new Object() { ...,我们会得到编译错误,因为Object没有任何fieldmethod()成员。

相关问题