Здравствуйте!
В Java у класса Class есть метод getDeclaredConstructors(). В JavaDoc'е к этому методу есть комментарий:
This method returns an array of length 0 if this Class object represents an interface, a primitive type, an array class, or void.
Звучит вполне логично. Проверяем:
System.out.println("Interface: " + Runnable.class.getDeclaredConstructors().length);
System.out.println("Primitive: " + int.class.getDeclaredConstructors().length);
System.out.println("Array: " + int[].class.getDeclaredConstructors().length);
System.out.println("void: " + void.class.getDeclaredConstructors().length);
Выдает:
Interface: 0
Array: 0
Primitive: 0
void: 0
Все ок.
На Хабре недавно был список задач по Джаве и один из вопросов как раз был «Может ли класс в Java не иметь конструкторов?».
В качестве решения было предложено:
public class Main {
static class Nested {
private Nested() {}
}
public static void main(String[] args) throws ClassNotFoundException {
new Nested();
System.out.println(" Main$1: " + Class.forName("Main$1").getDeclaredConstructors().length);
}
}
Выдает 0.
Почему так?
$1 как бы намекает, что это анонимный класс.
Но, во-первых, про анонимные классы в JavaDoc'е ни слова.
Во-вторых, проверим:
System.out.println(" Serializable: " + new Serializable(){}.getClass().getDeclaredConstructors().length +
", isAnonymousClass(): " + new Serializable(){}.getClass().isAnonymousClass());
// печатает 'Serializable: 1, isAnonymousClass(): true'
Т.е. наш анонимный класс имеет 1 конструктор.
Проверим для решения из Хабра, является ли тот класс анонимным:
System.out.println(" Main$1: " + Class.forName("Main$1").getDeclaredConstructors().length +
", isAnonymousClass(): " + Class.forName("Main$1").isAnonymousClass());
// печатает 'Main$1: 0, isAnonymousClass(): true'
Копаем дальше:
System.out.println(" Main$1: " + Class.forName("Main$1").getDeclaredConstructors().length +
", isAnonymousClass(): " + Class.forName("Main$1").isAnonymousClass() +
", isInterface(): " + Class.forName("Main$1").isInterface() +
", isPrimitive(): " + Class.forName("Main$1").isPrimitive() +
", isArray(): " + Class.forName("Main$1").isArray() +
", isEnum(): " + Class.forName("Main$1").isEnum() +
", isLocalClass(): " + Class.forName("Main$1").isLocalClass() +
", isAnnotation(): " + Class.forName("Main$1").isAnnotation() +
", isSynthetic(): " + Class.forName("Main$1").isSynthetic() +
", isMemberClass(): " + Class.forName("Main$1").isMemberClass());
Выдает:
Main$1: 0,
isAnonymousClass(): true,
isInterface(): false,
isPrimitive(): false,
isArray(): false,
isEnum(): false,
isLocalClass(): false,
isAnnotation(): false,
isSynthetic(): true,
isMemberClass(): false
Ага, видим, что класс синтетический:
indicates that this class or interface was generated by a compiler and does not appear in source code.
Т.е. синтетические - это классы/методы/конструкторы, которые компилятор создает сам, которых нет в исходниках.
Как известно, компилятор знает только о top-level классах.
Он ничего не знает про nested, inner и тп.
Для их взаимодействия, например, он и создает синтетические классы, методы и конструкторы.
Все встает на свои места.
Но, если мы выполним:
public class Main {
static class Nested {
private Nested() {}
}
public static void main(String[] args) throws ClassNotFoundException {
new Nested();
System.out.println(" Main$1: " + Class.forName("Main$1").getDeclaredConstructors().length +
", isAnonymousClass(): " + Class.forName("Main$1").isAnonymousClass() +
", isSynthetic(): " + Class.forName("Main$1").isSynthetic());
System.out.println(" Main$Nested: " + Class.forName("Main$Nested").getDeclaredConstructors().length +
", isAnonymousClass(): " + Class.forName("Main$Nested").isAnonymousClass() +
", isSynthetic(): " + Class.forName("Main$Nested").isSynthetic());
System.out.println(" Serializable: " + new Serializable(){}.getClass().getDeclaredConstructors().length +
", isAnonymousClass(): " + new Serializable(){}.getClass().isAnonymousClass() +
", isSynthetic(): " + new Serializable(){}.getClass().isSynthetic());
}
}
То получим:
Main$1: 1, isAnonymousClass(): true, isSynthetic(): false
Main$Nested: 2, isAnonymousClass(): false, isSynthetic(): false
Serializable: 1, isAnonymousClass(): true, isSynthetic(): false
Если добавляю Main$2, Main$3, то они равны Main$1 (вангую, что это три анонимных Serializable).
Main$4 класса нет. Кидает эксепшн.
Вопрос 1: Куда делся синтетический класс без конструкторов? Nested класс же остался.
Вопрос 2: Почему Main$Nested имеет 2 конструктора? Один - default. А второй?
Вопрос 3: Можете еще привести примеры synthetic's? Нагуглил что-то про switch-statement, но воспроизвести не удалось.
Извините за простыню. Спасибо.