动态构造匿名类混淆 [英] Dynamic construction of anonymous class confusion

查看:140
本文介绍了动态构造匿名类混淆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图使用反射来创建匿名类的实例。但实际上我在实例化过程中看到了奇怪的行为。

I'm trying to make instances of anonymous classes using reflection. But ocassionally I've seen strange behaviour during instantination.

请看看这些类似的代码片段

Please, look at these similar fragments of code

public class HideAndSeek {

    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws IllegalAccessException, InstantiationException{

        final String finalString = "I'm final :)";

        Object object2 = new Object(){

            {
                System.out.println("Instance initializing block");
                System.out.println(finalString);
            }           

            private void hiddenMethod() {
                System.out.println("Use reflection to find me :)");
            }
        };

        Object tmp = object2.getClass().newInstance();
    }

}

此代码运行良好,输出预期

This code works well, and the output expected

Instance initializing block
I'm final :)
Instance initializing block
I'm final :)

之后,我决定以简单的方式更改代码java.util.Calendar)

After this I've decided to change code in simple way (just added java.util.Calendar)

import java.util.Calendar;

    public class HideAndSeek {

        @SuppressWarnings("unchecked")
        public static void main(String[] args) throws IllegalAccessException, InstantiationException{

            final String finalString = "I'm final :)";

            final Calendar calendar = Calendar.getInstance();
            System.out.println(calendar.getTime().toString()); //works well

            Object object2 = new Object(){

                {
                    System.out.println("Instance initializing block");
                    System.out.println(finalString);

                    //simply added this line
                    System.out.println(calendar.getTime().toString());
                }           

                private void hiddenMethod() {
                    System.out.println("Use reflection to find me :)");
                }
            };

            Object tmp = object2.getClass().newInstance();
        }

    }

ve得到:

Wed Aug 17 02:08:47 EEST 2011
Instance initializing block
I'm final :)
Wed Aug 17 02:08:47 EEST 2011
Exception in thread "main" java.lang.InstantiationException: HideAndSeek$1
    at java.lang.Class.newInstance0(Unknown Source)
    at java.lang.Class.newInstance(Unknown Source)
    at HideAndSeek.main(HideAndSeek.java:29)

如您所见 - 尚未创建新实例。

As you may see - new instance hasn't been created.

任何人都可以向我解释, ?

Could anybody explain me, the reason of such changes?

感谢

推荐答案

这是一个非常简单的问题,非常复杂的答案。

This is a very simple question with a very complex answer. Please bear with me as I try to explain it.

查看在中引发异常的源代码(我不知道为什么你的堆栈跟踪不给出 Class 中的行号):

Looking at the source code where the exception is raised in Class (I'm not sure why your stack trace doesn't give the line numbers in Class):

try
{
  Class[] empty = {};
  final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
  // removed some code that was not relevant
}
catch (NoSuchMethodException e)
{
  throw new InstantiationException(getName());
}

您会看到 NoSuchMethodException 正在重新引用为 InstantiationException 。这意味着 object2 的类类型没有无参构造函数。

you see that NoSuchMethodException is being rethrown as InstantiationException. This means there is not a no-arg constructor for the class type of object2.

首先, object2 是什么类型?使用代码

First, what type is object2? With the code

System.out.println("object2 class: " + object2.getClass());

我们看到


object2 class:class junk.NewMain $ 1

object2 class: class junk.NewMain$1

这是正确的(我在package junk中运行示例代码,NewMain类)

which is correct (I run sample code in package junk, class NewMain).

junk.NewMain $ 1

Class obj2Class = object2.getClass();
try
{
  Constructor[] ctors = obj2Class.getDeclaredConstructors();
  for (Constructor cc : ctors)
  {
    System.out.println("my ctor is " + cc.toString());
  }
}
catch (Exception ex)
{
  ex.printStackTrace();
}

my ctor is junk.NewMain $ 1(java.util.Calendar)

my ctor is junk.NewMain$1(java.util.Calendar)

正在寻找日历以传入。这将为您工作:

So your anonymous class is looking for a Calendar to be passed in. This will then work for you:

Object newObj = ctors[0].newInstance(Calendar.getInstance());

如果你有这样的:

final String finalString = "I'm final :)";
final Integer finalInteger = new Integer(30);
final Calendar calendar = Calendar.getInstance();
Object object2 = new Object()
{
  {
    System.out.println("Instance initializing block");
    System.out.println(finalString);
    System.out.println("My integer is " + finalInteger);
    System.out.println(calendar.getTime().toString());
  }
  private void hiddenMethod()
  {
    System.out.println("Use reflection to find me :)");
  }
};

那么我调用 newInstance 因为现在它需要:

then my call to newInstance won't work because there are not enough arguments in the ctor, because now it wants:


我的ctor是junk.NewMain $ 1(java.lang。 Integer,java.util.Calendar)

my ctor is junk.NewMain$1(java.lang.Integer,java.util.Calendar)

如果我用

Object newObj = ctors[0].newInstance(new Integer(25), Calendar.getInstance());

使用调试器可以看到 finalInteger 是25而不是最终值30.

a peek inside using the debugger shows that finalInteger is 25 and not the final value 30.

事情有点复杂,因为你在静态上下文中做所有上述。如果你把所有你的代码和移动它的非静态方法,像这样(记住,我的类是junk.NewMain):

Things are slightly complicated because you're doing all of the above in a static context. If you take all your code above and move it into a non-static method like so (remember, my class is junk.NewMain):

public static void main(String[] args)
{
  NewMain nm = new NewMain();
  nm.doIt();
}

public void doIt()
{
  final String finalString = "I'm final :)";
  // etc etc
}

内部类现在(删除我添加的Integer引用):

you'll find the ctor for your inner class is now (removing my added Integer reference):


my ctor是junk.NewMain $ 1(junk.NewMain,java.util。日历)

my ctor is junk.NewMain$1(junk.NewMain, java.util.Calendar)

Java语言规范 15.9.3 部分解释这样:


如果C是一个匿名类,而C,S的直接超类是一个
内部类,则:

If C is an anonymous class, and the direct superclass of C, S, is an inner class, then:


  • 如果S是一个本地类,S出现在静态上下文中,那么
    参数

  • 否则,直接包含的i相对于$ b $的实例,如果有的话,它是
    的构造函数的参数。 b S是构造函数的第一个参数,后面是创建
    表达式的类实例的参数列表中的
    参数,如果有的话,按照它们在表达式中出现的顺序。

为什么匿名构造函数接受参数?

Why does the anonymous constructor take arguments at all?

因为你不能为一个匿名内部类创建一个构造函数,所以实例初始化块实现这个目的(记住,你只有一个匿名内部类的实例)。 VM不知道内部类,因为编译器将所有内容分离为单独的类(例如,junk.NewMain $ 1)。该类的ctor包含实例初始化程序的内容。

Since you can't create a constructor for an anonymous inner class, the instance initializer block serves that purpose (remember, you only have one instance of that anonymous inner class). The VM has no knowledge of the inner class as the compiler separates everything out as individual classes (e.g. junk.NewMain$1). The ctor for that class contains the contents of the instance initializer.

这是由JLS 15.9.5.1匿名构造函数


。 ..匿名构造函数为每个实际
参数的一个形式参数,其中C是
声明的类实例创建表达式。

...the anonymous constructor has one formal parameter for each actual argument to the class instance creation expression in which C is declared.

您的实例初始化程序引用了 Calendar 对象。除了通过构造函数之外,编译器还要如何将那个运行时值传递到你的内部类(它被创建为一个VM的类)?

Your instance initializer has a reference to a Calendar object. How else is the compiler going to get that runtime value into your inner class (which is created as just a class for the VM) except through the constructor?

),对最后一个灼热的问题的答案。为什么构造函数不需要 String ? JLS的最后一个位 3.10.5 说明:

Finally (yay), the answer to the last burning question. Why doesn't the constructor require a String? The last bit of JLS 3.10.5 explains that:


通过常量表达式计算的字符串在编译时计算
,然后视为它们是文字。

Strings computed by constant expressions are computed at compile time and then treated as if they were literals.

换句话说,您的 String 值在编译时是已知的,因为它是一个文字,需要成为匿名构造函数的一部分。为了证明这一点,我们将测试JLS 3.10.5中的下一条语句:

In other words, your String value is known at compile time because it's a literal so it does not need to be part of the anonymous constructor. To prove this is the case we'll test the next statement in JLS 3.10.5:


在运行时通过连接计算的字符串是新的

Strings computed by concatenation at run time are newly created and therefore distinct.

因此更改您的代码:

String str1 = "I'm";
String str2 = " final!";
final String finalString = str1 + str2

,你会发现你的ctor现在是在非静态上下文中):

and you'll find your ctor is now (in the non-static context):


my ctor是junk.NewMain $ 1(junk.NewMain,java.lang.String,java。 util.Calendar)

my ctor is junk.NewMain$1(junk.NewMain,java.lang.String,java.util.Calendar)

我希望这是有道理的,是有帮助的。我学到了很多,这是肯定的!

Phew. I hope this makes sense and was helpful. I learned a lot, that's for sure!

这篇关于动态构造匿名类混淆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆