This is a very simple question with a very complex answer. Please bear with me as I try to explain it.
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());
}
you see that NoSuchMethodException
is being rethrown as InstantiationException
. This means there is not a no-arg constructor for the class type of object2
.
First, what type is object2
? With the code
System.out.println("object2 class: " + object2.getClass());
we see that
object2 class: class junk.NewMain$1
which is correct (I run sample code in package junk, class NewMain).
What then are the constructors of 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();
}
which gives us
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());
If you have something like this:
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 :)");
}
};
then my call to newInstance
won't work because there are not enough arguments in the ctor, because now it wants:
my ctor is junk.NewMain$1(java.lang.Integer,java.util.Calendar)
If I then instantiate that with
Object newObj = ctors[0].newInstance(new Integer(25), Calendar.getInstance());
a peek inside using the debugger shows that finalInteger
is 25 and not the final value 30.
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
}
you'll find the ctor for your inner class is now (removing my added Integer reference):
my ctor is junk.NewMain$1(junk.NewMain, java.util.Calendar)
The Java Language Specification, section 15.9.3 explains it this way:
If C is an anonymous class, and the direct superclass of C, S, is an
inner class, then:
- If the S is a local class and S occurs in a static context, then
the arguments in the argument list, if any, are the arguments to
the constructor, in the order they appear in the expression.
- Otherwise, the immediately enclosing instance of i with respect to
S is the first argument to the constructor, followed by the
arguments in the argument list of the class instance creation
expression, if any, in the order they appear in the expression.
Why does the anonymous constructor take arguments at all?
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.
This is explayed by JLS 15.9.5.1 Anonymous Constructors:
...the anonymous constructor has one formal parameter for each actual
argument to the class instance creation expression in which C is
declared.
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?
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.
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.
Change your code thusly:
String str1 = "I'm";
String str2 = " final!";
final String finalString = str1 + str2
and you'll find your ctor is now (in the non-static context):
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!