Это очень простой вопрос с очень сложным ответом.Пожалуйста, потерпите меня, когда я пытаюсь это объяснить.
Глядя на исходный код, где возникает исключение в Class
(я не уверен, почему ваша трассировка стека не дает номера строк в 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
нет конструктора без аргументов.
Во-первых, какой тип object2
?С кодом
System.out.println("object2 class: " + object2.getClass());
мы видим, что
object2 class: class junk.NewMain $ 1
, что правильно (я запускаю пример кода впакет мусора, класс 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();
}
, что дает нам
мой ctor - мусор. NewMain$ 1 (java.util.Calendar)
Таким образом, ваш анонимный класс ищет для передачи Calendar
. Это будет работать для вас:
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
не будет работать, потому что в ctor недостаточно аргументов, потому что теперь он хочет:
мой ctor - мусор. NewMain $ 1 (java.lang.Integer, java.util.Calendar)
Если я тогда создаю это с
Object newObj = ctors[0].newInstance(new Integer(25), Calendar.getInstance());
, заглянем внутрь с помощью отладчикапоказывает, что finalInteger
равно 25, а не конечное значение 30.
Все немного сложнее, потому что вы делаете все вышеперечисленное в статическом контексте.Если вы возьмете весь приведенный выше код и переместите его в нестатический метод, например, так (помните, мой класс - нежелательный. NewMain):
public static void main(String[] args)
{
NewMain nm = new NewMain();
nm.doIt();
}
public void doIt()
{
final String finalString = "I'm final :)";
// etc etc
}
вы обнаружите, что ctor для вашего внутреннего класса теперь(удаляя мою добавленную целочисленную ссылку):
мой ctor является ненужным. NewMain $ 1 (junk.NewMain, java.util.Calendar)
язык JavaСпецификация , раздел 15.9.3 объясняет это следующим образом:
Если C является анонимным классом, а прямой суперкласс C, S является внутренним классом,затем:
- Если S является локальным классом и S встречается в статическом контексте, то аргументы в списке аргументов, если они есть, являются аргументами конструктора в том порядке, в котором они отображаются ввыражение.
- В противном случае немедленно включающий экземпляр i по отношению к S является первым аргументом конструктора, за которым следуют аргументы в списке аргументов выражения создания экземпляра класса, если оно есть, в порядкеони появляются в выражении.
Почему анонимный конструктор вообще принимает аргументы?
Поскольку вы не можете создать конструктор для анонимного внутреннего класса, блок инициализатора экземпляра служит этой цели (помните,у вас есть только один экземпляр этого анонимного внутреннего класса).Виртуальная машина не знает внутреннего класса, так как компилятор выделяет все как отдельные классы (например, junk.NewMain $ 1).Ctor для этого класса содержит содержимое инициализатора экземпляра.
Это выполняется JLS 15.9.5.1 Анонимные конструкторы :
... анонимныйКонструктор имеет один формальный параметр для каждого фактического аргумента в выражении создания экземпляра класса, в котором объявлен C.
Ваш инициализатор экземпляра имеет ссылку на Calendar
объект.Как еще компилятор собирается получить это значение времени выполнения в ваш внутренний класс (который создается как просто класс для ВМ), кроме как через конструктор?
Наконец (ура), ответ на последний актуальный вопрос,Почему конструктор не требует String
?Последний бит JLS 3.10.5 объясняет, что:
Строки, вычисленные с помощью константных выражений, вычисляются во время компиляции, а затем обрабатываются так, как если бы они были литералами.
Другими словами, ваше значение String
известно во время компиляции, потому что оно является литералом, поэтому оно не обязательно должно быть частью анонимного конструктора.Чтобы доказать это, мы протестируем следующий оператор в JLS 3.10.5:
Строки, вычисленные путем конкатенации во время выполнения, создаются заново и поэтому различаются.
Измените ваш код следующим образом:
String str1 = "I'm";
String str2 = " final!";
final String finalString = str1 + str2
и вы обнаружите, что ваш ctor сейчас (в нестатическом контексте):
мой ctor - мусорный. NewMain $ 1 (junk.NewMain, java.lang.String, java.util.Calendar)
Фу.Я надеюсь, что это имеет смысл и было полезно.Я многому научился, это точно!