Как Java различает эти несколько методов с тем же именем/сигнатурой?
Сегодня я отслеживал ошибку, и я заметил что-то странное в одном из наших классов. Я вырезал как можно больше кода, чтобы опубликовать здесь:
class A {
static int obtainNumber() { return 42; }
static int obtainNumber() { return 3; }
static int obtainNumber() { return -1; }
static {
System.out.println(obtainNumber());
}
}
Этот класс имеет 3 метода с одинаковым именем и сигнатурой. Сначала я думал, что это неверный код, но затем затмение выделило бы код красным. Он работает:
javac A.java && java A
42
Exception in thread "main" java.lang.NoSuchMethodError: main
Итак, я подумал, что, возможно, Java будет использовать только тот, который он видит. Я переупорядочился, чтобы проверить:
class A {
static int obtainNumber() { return 3; }
static int obtainNumber() { return -1; }
static int obtainNumber() { return 42; }
static {
System.out.println(obtainNumber());
}
}
Нет, тот же результат:
javac A.java && java A
42
Exception in thread "main" java.lang.NoSuchMethodError: main
Я подумал, что, возможно, он использует тот, у которого 42, потому что его самый большой. Чтобы проверить это, я взял оригинал и изменил возвращаемые значения:
class A {
static int obtainNumber() { return 0; }
static int obtainNumber() { return 1; }
static int obtainNumber() { return 2; }
static {
System.out.println(obtainNumber());
}
}
Он все еще знает, как использовать первую:
javac A.java && java A
0
Exception in thread "main" java.lang.NoSuchMethodError: main
И если я снова их переупорядочу:
class A {
static int obtainNumber() { return 1; }
static int obtainNumber() { return 0; }
static int obtainNumber() { return 2; }
static {
System.out.println(obtainNumber());
}
}
Тот же результат:
javac A.java && java A
0
Exception in thread "main" java.lang.NoSuchMethodError: main
Я думал, что Java - это текстовый язык, который, я ожидаю, делает невозможным такое. Как отслеживается Java, какой метод является?
Ответы
Ответ 1
Скрытые символы. Исходный код эквивалентен
static int obtainNumber() { return 42; }
static int obtain\ufeffNumber() { return 3; }
static int obtain\ufeff\ufeffNumber() { return -1; }
Чтобы избежать подобных проблем, мои исходные файлы - это строго US-ASCII. Я хочу быть уверенным, что символы, которые я вижу, точно компилятор символов видит.
Ответ 2
Я только что скопировал/вставил это в свою IDE, и хотя это было чудо, тогда при попытке сохранить файл появилось сообщение об ошибке:
Сохранить не удалось. Попробуйте Файл > Сохранить как... если проблема не устранена.
Причина: Некоторые символы не могут быть сопоставлены с использованием кодировки "Cp1252". Либо измените кодировку, либо удалите символы, которые не поддерживаются кодировкой "Cp1252".
Итак, эти методы не имеют одинакового имени, просто используйте символы, которые выглядят одинаково.
Дополнительная информация, связанная с кодировкой символов в исходных файлах Java:
Ответ 3
Мой оригинальный комментарий:
Если вы заметили, тот, который был взят, - это тот, у которого разный синтаксис, связанный с другими. Возможно, это может вас подсказать на основе скрытых символов или грамматических причуд.
Я вложил это в Eclipse и заметил немного лишнего персонажа. Я выбросил сохраненный файл (в CP1252) в шестнадцатеричный редактор и нашел отметку порядка байтов.
Когда я посмотрел, CP1252 не имеет знака порядка байтов, но символы сами находятся в CP1252. Возможно, появился паразитный символ юникода.
Когда я посмотрел более внимательно, другой метод имел еще один байтовый порядок в другом порядке байтов.
Как они пришли, мы никогда не узнаем. Однако мы знаем, что компилятор берет тот, у которого нет знака порядка байтов.
Вы должны проверить проект на предмет проблем с кодировкой, особенно если он был восстановлен из других системных типов или старых носителей данных, которые могут быть неисправными или подверглись воздействию устаревшего оборудования.
Нам действительно нужно иметь дело с этим поведением FGITW через meta sometime.