Вопрос или проблема
Документация по методу constructor.getParameterTypes() говорит следующее:
… Обратите внимание, что конструкторы некоторых внутренних классов могут иметь неявно объявленный параметр в дополнение к явно объявленным.
Как говорится, я попытался использовать это для внутреннего класса с конструктором без параметров, и он дал мне конструктор с одним параметром (внешним классом, пример ниже). Есть ли простой способ получить только явные параметры?
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.*;
public class TestInner{
public class Inner{}
public static void main(String[] args) throws ClassNotFoundException {
Class<?> innerClass = Class.forName("TestInner$Inner");
Constructor<?>[] constructors = innerClass.getConstructors();
String str = Arrays.stream(constructors).map(TestInner::stringifyConstructor).collect(Collectors.joining(", "));
System.out.println(str);
}
public static String stringifyConstructor(Constructor c){
return "("+Arrays.stream(c.getParameterTypes()).map(e->e.getName()).collect(Collectors.joining(", "))+")";
}
}
Я понимаю, что я могу просто убрать первый элемент, но как мне знать, что первый элемент на 100% неявный? Кажется, что не все внутренние классы имеют этот параметр (потому что в документации сказано “некоторые внутренние классы могут”, и внутренние классы, если я правильно понимаю, означают нестатические вложенные классы). Есть ли какие-либо другие факторы (кроме того, чтобы не быть статическим), которые могут привести к отсутствию этого параметра?
Ответ или решение
Как получить только явные параметры конструктора внутреннего класса в Java
При работе с конструкторами внутреннего класса в Java возникают некоторые нюансы, о которых стоит знать. В частности, документация к методу Constructor.getParameterTypes()
подчеркивает, что конструкторы некоторых внутренних классов могут иметь неявно объявленный параметр, помимо явно определяемых. Это означает, что в случае, если вы работаете с внутренними классами, вы можете столкнуться с ситуацией, когда список параметров конструктора будет включать в себя ссылку на внешний класс.
Пример:
Рассмотрим следующий код:
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.*;
public class TestInner {
public class Inner {}
public static void main(String[] args) throws ClassNotFoundException {
Class<?> innerClass = Class.forName("TestInner$Inner");
Constructor<?>[] constructors = innerClass.getConstructors();
String str = Arrays.stream(constructors)
.map(TestInner::stringifyConstructor)
.collect(Collectors.joining(", "));
System.out.println(str);
}
public static String stringifyConstructor(Constructor<?> c) {
return "(" + Arrays.stream(c.getParameterTypes())
.map(e -> e.getName())
.collect(Collectors.joining(", ")) + ")";
}
}
Проблема:
Запуская приведенный выше код, вы можете заметить, что конструктор класса Inner
показывает наличие одного параметра — ссылки на экземпляр внешнего класса TestInner
. Это связано с тем, что внутренние классы в Java (не статические) всегда имеют неявный параметр, который указывает на родительский класс.
Как получить только явные параметры?
Чтобы получить только явно определенные параметры конструктора, необходимо учитывать, что первый параметр указывает на внешний класс. Однако, как вы верно заметили, не все внутренние классы могут иметь этот неявный параметр, поскольку он относится только к нем статическим вложенным классам. На практике, следующая логика позволит отфильтровать неявные параметры:
-
Проверка: Если класс является ненастатическим внутренним классом, то первым параметром будет являться его внешний класс. Вы можете проверить это, сверив имя первого параметра.
-
Фильтрация: Если первый параметр действительно является экземпляром внешнего класса, то его можно удалить.
Реализация:
Используем модифицированный метод stringifyConstructor
, чтобы исключать первым параметром экземпляр внешнего класса, если это действительно параметр внешнего класса:
public static String stringifyConstructor(Constructor<?> c) {
Class<?>[] paramTypes = c.getParameterTypes();
List<String> paramNames = Arrays.stream(paramTypes)
.map(e -> e.getName())
.collect(Collectors.toList());
// Проверяем, является ли первый параметр типом внешнего класса
if (paramNames.size() > 0 &&
paramNames.get(0).equals("TestInner")) { // Замените на актуальное имя вашего внешнего класса
paramNames.remove(0); // Убираем первый параметр
}
return "(" + String.join(", ", paramNames) + ")";
}
Заключение
Таким образом, работа с конструкторами внутренних классов в Java требует тщательного подхода к фильтрации параметров. Используя предложенную технику, вы можете получить только явные параметры конструкторов внутренних классов, избегая путаницы с неявными ссылками на внешние классы. Эта информация полезна для разработки, когда необходимо анализировать классы и их структуру во время выполнения программы, сохранять чистоту интерфейса API и оптимизировать читаемость кода.