简体   繁体   English

访问匿名内部类变量

[英]Access anonymous inner class variables

How to access i from the outer class? 如何从外部类访问我?

  HashSet<Integer> hs=new HashSet<Integer>(){
        int i=30;
    };

I can do it like this 我可以这样做

int k=new HashSet<Integer>(){
    int i=30;
}.i;

But if I get 'i' then I cannot get the instance of hashset.Is there a way to get both?The question just out of curiosity.It doesn't have much practical applications.I just want to know whether it can be done. 但是,如果我得到“我”,那么我就无法得到hashset的实例。有没有办法获得两者?问题只是出于好奇。它没有太多实际应用。我只是想知道它是否可以完成。

Solution 1: Object registration system 解决方案1:对象注册系统

The two snippets presented essentially made a choice between storing the reference to the anonymous class instance just created, or to immediately access its custom field. 提供的两个片段实际上是在存储对刚刚创建的匿名类实例的引用或立即访问其自定义字段之间做出选择。 Doing one would seem to forfeit the ability to do the other. 做一个似乎会放弃做另一个的能力。

One way to achieve a compromise is to have an object registration system, and use an instance initializer ( JLS 8.6 ) in your anonymous class to self-register to this sytem. 实现折衷的一种方法是使用对象注册系统,并在匿名类中使用实例初始化程序JLS 8.6 )自行注册到此系统。

Here's a simple proof-of-concept, using Object[] for a simple registration system: 这是一个简单的概念验证,使用Object[]作为简单的注册系统:

    final Object[] registrar = { null };
    int k = new HashSet<Integer>(){
        { registrar[0] = this; }
        int i= 666;
    }.i;
    Set<Integer> set = (Set<Integer>) registrar[0];

    System.out.println(k); // 666
    System.out.println(set.getClass().isAnonymousClass()); // true

Essentially we use a 1-element Object[] , and in the instance initializer of our anonymous class, we "register" this to this array. 基本上我们使用1元素Object[] ,并且在我们的匿名类的实例初始化器中,我们this “注册”到此数组。 Note that this is just a proof-of-concept; 请注意,这只是一个概念验证; in production code, you'd use a more robust and typesafe object registration system than this. 在生产代码中,您将使用比此更强大且类型安全的对象注册系统。


Solution 2: Tuples 解决方案2:元组

This approach works nicely if your anonymous class may have multiple "fields". 如果您的匿名类可能有多个“字段”,则此方法很有效。 Simply pack them all into a tuple , making sure to include a reference to this in the anonymous class. 简单地收拾他们都变成一个元组 ,确保包括一个参考this在匿名类。

Here's a simple proof-of-concept, using Object[] for a tuple: 这是一个简单的概念验证,使用Object[]作为元组:

    Object[] tuple = new HashSet<Integer>(){
        Object[] tuple = { this, 666, 13, "XXX", "OMG ABUZE" };
    }.tuple;
    Set<Integer> set = (Set<Integer>) tuple[0];
    set.add(null);
    System.out.println(set.getClass().isAnonymousClass()); // true
    System.out.println(Arrays.toString(tuple));
    // [[null], 666, 13, XXX, OMG ABUZE]

In production code, you'd use a CustomTuple class that is more object-oriented and typesafe. 在生产代码中,您将使用更面向对象且类型安全的CustomTuple类。 Note that this is an analogous solution to the "how can my method return 2 values" problem: simply return 1 value that captures all of those information. 请注意,这是“我的方法如何返回2个值”问题的类似解决方案:只需返回1个值即可捕获所有这些信息。

Reflection to the rescue, I guess: 我想是对救援的反思:

HashSet<Integer> hs = new HashSet<Integer>() {
    int i = 30;
};
int i = hs.getClass().getDeclaredField("i").getInt(hs);
System.out.println(i);

PS I feel kind of dirty after writing that. PS写完之后我觉得有点脏。

It makes sense when you think about it. 当你想到它时,它是有道理的。

The expression 表达方式

new HashSet<Integer>(){
    int i=30;
}

is of type java.util.HashSet , and java.util.HashSet has no field called i , so java.util.HashSet类型, java.util.HashSet没有名为i字段,所以

new HashSet<Integer>(){
   int i=30;
}.i

will not compile. 不会编译。 Put another way, try breaking up the compound expression so the the result of the new is assigned to a variable x , then you call xi . 换句话说,尝试分解复合表达式,以便将new的结果赋给变量x ,然后调用xi What type could you give x that would allow xi to compile? 你可以给x什么类型的xi来编译? There is no such type name, it's an anonymous class. 没有这样的类型名称,它是一个匿名类。

This is a "downside" on anonymous classes - they can only meaningfully override members that exist in the type being extended. 这是匿名类的“缺点” - 它们只能有意义地覆盖扩展类型中存在的成员。 If you add new members, they can only be accessed via reflection. 如果添加新成员,则只能通过反射访问它们。

I don't believe you can. 我不相信你能。 You can't declare the type of a variable as an anonymous type 1 . 您不能将变量的类型声明为匿名类型1 You can introduce a named class within a method though: 您可以在方法中引入命名类:

import java.util.*;

public class Test
{
    public static void main(String[] args)
    {
        class Foo extends Hashtable
        {
            int i = 30;
        }

        Foo f = new Foo();
        System.out.println(f.i);
    }
}

It's horrible (really, really horrible) and I don't think I've ever seen it used in production code - but it works. 它太可怕了(真的,非常可怕)而且我认为我从未见过它在生产代码中使用过 - 但它确实有效。


1 C# gets around this using var - the compiler uses the initialization expression to determine the variable's type. 1 C#使用var解决这个问题 - 编译器使用初始化表达式来确定变量的类型。 Anonymous types in Java are somewhat different to those in C#, but if Java had something like var , I believe your code would work. Java中的匿名类型与C#中的类型有些不同,但如果Java有类似var东西,我相信你的代码会起作用。

Well, to my mind, there is always the reflection way, but it seems a little overkill to me. 嗯,在我看来,总会有反思的方式,但对我来说似乎有点矫枉过正。

And as you set the class to be anonymous way, I cannot see any other way to do it than yours. 当你将课程设置为匿名方式时,我看不到任何其他方式来做你的工作。

Annonymous inner classes weren't designed for that purpose, so from what I remember there's no way you could do that. 匿名内部课程不是为此目的而设计的,所以从我记忆中,你无法做到这一点。 If you want you can create a "normal" inner class. 如果你想要,你可以创建一个“正常”的内部类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM