传递匿名类

8

我正在将一个接口作为匿名实现传递给另一个对象,如下所示:

public interface Interface {
    public int convert (int a);
}



public static void main(String[] args) throws IOException, InterruptedException {

    final int[] array = {1,6,3,5,7,8,4,0,3};
    Interface inter = new Interface() {         
        public int convert(int a) {
            int result = a;             
            for (int i = 0; i < array.length; i++) {
                a=a+array[i];
            }               
            return a;
        }
    };

    SomeObject ty = new SomeObject ();
    ty.Test(7, inter);
}

public class SomeObject {   

    public void Test(int number, Interface inter) {             
        System.out.println(inter.convert(number));
    }
}

我的问题是:它是如何工作的?SomeObject 如何知道数组,而该数组并未直接传递给对象(该数组不是匿名类的成员)。

更新
(抱歉更新晚了)

那么匿名类中使用的成员变量或方法呢?它们不是final。

Interface inter = new Interface() {         
    public int convert(int a) {
        int result = a + someMemberVar;             
        for (int i = 0; i < array.length; i++) {
            a=a+array[i];
        }               
        return a;
    }
};

1
“int result = a;” 这行代码的目的是什么? - Mark Byers
6个回答

2
我的问题是它是如何工作的?SomeObject 如何知道关于数组的信息,因为该数组并未直接传递给该对象(数组不是匿名类的成员)。
答案:除非您将数组声明为 final,否则不会。原因是(如果数组为 final),由于匿名函数知道数组不能更改,因此它可以像常量一样处理。
那么成员变量或在匿名类中使用的方法呢?它们并不是 final。
答案:实例变量和方法(以及静态变量和方法)在创建匿名类时仍然处于范围内。

所以整个数组以其当前状态作为常量传递给SomeObject? - omrid
1
只有对数组对象的引用被传递给匿名类。如果声明方法更改了数组elements,则匿名类将看到这一点。 - Christian Semrau
@Jonathan,你能详细解释一下可见性吗?它不起作用,因为它是final的。-1(一旦解决了可见性问题,将删除) - Stefan

2
由于方法局部变量是直接在方法块内声明的,因此它仅在该方法内可见,从声明之后的下一条语句开始。这里所说的方法局部变量是指在方法内部但不在任何其他块(例如while { /* not here */ })中声明的变量。
如果您看到,您的匿名类也是在array声明之后在同一方法中定义为本地类。这就是为什么它对该类可见的原因。
需要使array成为final才能在匿名类中使用的原因是,局部变量仅存在于方法存在的时间内,但是本地类的对象可能存在更长的时间(即使方法完成后)。

可见性实际上也受到代码块的限制,因此它不仅限于方法内的局部变量,还必须在同一个代码块内。 - Stefan
@Stefan:谢谢。我把它从仅局部变量改成了方法局部变量。 - Bhesh Gurung

1

这是Java的作用域和可见性规则的一部分。在您当前的示例中,它被称为闭包。基本上,在块中定义的所有内容都在该块内可见。例如,以下是有效的Java代码(在方法的任何位置):

{
    final int i = 5;
    // do somthing with i
}
// cant refernce i here
{
    final int i = 6;
    // do somthing with i
}
// cant refernce i here

由于您在块内定义了一个新类(如果您只是实例化它,则无法工作),因此它可以看到在同一块中声明的所有内容。 Java对此的唯一限制是,您不能在第一次赋值后更改该值,以避免引用“逃逸”其块(多线程,生存期长于声明块等)时出现问题。如果将方法的参数声明为final,则也可以使用它们,因为它们在同一块中定义。

因此,如果您将代码更改为以下内容

      {
          final int[] array = {1,6,3,5,7,8,4,0,3};
      }
      Object inter = new Object() 
      {           
          public void test() 
          {
              System.out.println(array);
          }
      };

这样做是行不通的(试一下吧)。


0

你正在将具有对数组访问权限的Interface对象传递到SomeObject方法中。

因此,SomeObject正在使用你在主函数中传递的同一个Interface对象。

由于Interface对象具有对数组的访问权限,因此可以使用它。

使用数组的不是SomeObject,而是Interface对象本身。


匿名类可以访问其封闭方法的 final 变量。 - pringi

0

0
Java在内部类中使用的所有(final)封闭类对象保持引用。如果您想知道这个技巧是如何实现的,请将以下行添加到Test方法中:
System.out.println(inter.getClass().getDeclaredConstructors()[0].getParameterTypes()[0].getCanonicalName()

你的输出将是 "int[]"

因此,编译器会简单地为内部类创建一个构造函数,并将其需要的字段传递给它


我移除了最后的相关内容。 - Pablo Grisafi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接