5

如何从外部类访问 i?

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

我可以这样做

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

但是如果我得到'i'那么我就无法得到hashset的实例。有没有办法同时得到这两者?这个问题只是出于好奇。它没有太多实际应用。我只是想知道它是否可以完成.

4

6 回答 6

7

解决方案一:对象注册系统

呈现的两个片段本质上是在存储对刚刚创建的匿名类实例的引用或立即访问其自定义字段之间做出选择。做一个似乎会丧失做另一个的能力。

实现折衷的一种方法是拥有一个对象注册系统,并在您的匿名类中使用实例初始化程序( JLS 8.6 ) 来自行注册到该系统。

这是一个简单的概念验证,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

本质上,我们使用 1-element Object[],并且在匿名类的实例初始化程序中,我们“注册”this到这个数组。请注意,这只是一个概念验证;在生产代码中,您将使用比这更健壮和类型安全的对象注册系统。


解决方案 2:元组

如果您的匿名类可能有多个“字段”,这种方法效果很好。只需将它们全部打包到一个tuple中,确保this在匿名类中包含对的引用。

这是一个简单的概念验证,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]

在生产代码中,您将使用CustomTuple更面向对象和类型安全的类。请注意,这是“我的方法如何返回 2 个值”问题的类似解决方案:只需返回 1 个值即可捕获所有这些信息。

于 2010-08-18T07:43:24.917 回答
5

反思救援,我猜:

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

PS 写完之后感觉有点脏。

于 2010-08-18T07:31:14.383 回答
2

当你考虑它时,它是有道理的。

表达方式

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

是类型的java.util.HashSet,并且java.util.HashSet没有被调用的字段i,所以

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

不会编译。换句话说,尝试分解复合表达式,以便将 的结果new分配给变量x,然后调用x.i。你能给出什么类型来x允许x.i编译?没有这样的类型名称,它是一个匿名类。

这是匿名类的“缺点”——它们只能有意义地覆盖存在于被扩展类型中的成员。如果添加新成员,则只能通过反射访问它们。

于 2010-08-18T07:29:10.347 回答
1

我不相信你可以。您不能将变量的类型声明为匿名类型1。不过,您可以在方法中引入命名类:

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);
    }
}

这太可怕了(真的,真的太可怕了),我认为我从未见过它在生产代码中使用过——但它确实有效。


1 C# 解决了这个问题var- 编译器使用初始化表达式来确定变量的类型。Java 中的匿名类型与 C# 中的匿名类型有些不同,但如果 Java 有类似的东西var,我相信你的代码会起作用。

于 2010-08-18T07:27:19.600 回答
0

好吧,在我看来,总是有反射的方式,但对我来说似乎有点矫枉过正。

而且当您将课程设置为匿名方式时,除了您的之外,我看不到任何其他方式。

于 2010-08-18T07:26:45.000 回答
0

匿名内部类不是为此目的而设计的,所以据我所知,你不可能做到这一点。如果你愿意,你可以创建一个“正常”的内部类。

于 2010-08-18T07:28:46.207 回答