Статический импорт Java
Просто экспериментально я обнаружил, что нестатические методы Java переопределяют все методы с одинаковыми именами в области видимости даже в статическом контексте. Даже без перегрузки параметров. подобно
import java.util.Arrays;
import static java.util.Arrays.toString;
public class A {
public static void bar(Object... args) {
Arrays.toString(args);
toString(args); //toString() in java.lang.Object cannot be applied to (java.lang.Object[])
}
}
Я не могу найти ничего об этом в спецификации. Это ошибка? Если это не так, есть ли причины для внедрения такого языка?
UPD: Java 6 не компилирует этот пример. Вопрос - почему?
4 ответа
Объяснение простое, хотя оно не меняет того факта, что поведение крайне неинтуитивно:
При разрешении метода, который должен быть вызван, первое, что делает компилятор, - находит наименьшую вмещающую область, у которой есть метод с правильным именем. Только тогда появляются другие вещи, такие как разрешение перегрузки и совместная игра.
Теперь, что здесь происходит, это то, что наименьшая ограждающая область, которая содержит toString()
Метод класса А, который наследует его от Object
, Поэтому мы останавливаемся там и не ищем дальше. К сожалению, затем компилятор пытается найти лучшее соответствие методов в данной области и замечает, что он не может вызвать ни один из них, и выдает ошибку.
Это означает, что никогда не следует статически импортировать методы с именем, идентичным методу в Object, потому что методы, которые естественным образом находятся в области видимости, имеют приоритет над статическим импортом (JLS подробно описывает теневое копирование методов, но для этой проблемы я думаю, что гораздо проще просто помни это).
Редактировать: alf любезно предоставил правую часть JLS, которая описывает вызов метода для тех, кому нужна alf картинка. Это довольно сложно, но тогда проблема не проста, так что это следовало ожидать.
Это не переопределение. Если это сработало, this.toString()
все равно получит доступ к методу A
вместо Arrays.toString
как было бы в случае переопределения.
Спецификация языка объясняет, что статический импорт влияет только на разрешение static
методы и виды:
Одно объявление статического импорта d в модуле компиляции c пакета p, который импортирует поле с именем n, затеняет объявление любого статического поля с именем n, импортированного объявлением статического импорта по требованию в c, в течение c.
Одно объявление статического импорта d в модуле компиляции c пакета p, который импортирует метод с именем n с сигнатурой s, скрывает объявление любого статического метода с именем n с сигнатурой s, импортированного объявлением статического импорта по требованию в c на протяжении всего ок.
Одно объявление статического импорта d в модуле компиляции c пакета p, который импортирует тип с именем n, скрывает объявления:
- любой статический тип с именем n, импортированный объявлением статического импорта по требованию в c.
- любой тип верхнего уровня (§7.6) с именем n, объявленный в другом модуле компиляции (§7.3) из p.
- любой тип с именем n, импортированный с помощью объявления импорта типа по требованию (§7.5.2) в c. на протяжении всего ок.
Статический импорт не скрывает нестатические методы или внутренние типы.
Итак toString
не затеняет нестатический метод. Поскольку имя toString
может ссылаться на нестатический метод A
не может относиться к static
метод Arrays
и поэтому toString
привязывается к единственному методу с именем toString
что доступно в области, которая String toString()
, Этот метод не может принимать никаких аргументов, поэтому вы получаете ошибку компиляции.
Раздел 15.12.1 объясняет разрешение метода и должен был быть полностью переписан, чтобы позволить скрывать недоступные имена методов внутри static
методы но не внутри member
методы.
Я предполагаю, что разработчики языка хотели сохранить простые правила разрешения методов, что означает, что одно и то же имя означает одно и то же, независимо от того, встречается ли оно в static
метод или нет, и единственное, что изменения, которые доступны.
Если вы попытаетесь выполнить похожий код, вы не получите никакой ошибки компилятора.
import static java.util.Arrays.sort;
public class StaticImport {
public void bar(int... args) {
sort(args); // will call Array.sort
}
}
Причина, по которой это компилируется, а ваша нет, состоит в том, что toString()
(или любой другой метод, определенный в классе Object) по-прежнему ограничен классом Object, поскольку Object является родителем вашего класса. Следовательно, когда компилятор находит соответствующую сигнатуру этих методов из класса Object, он выдает ошибку компилятора. В моем примере, так как класс Object не имеет sort(int[])
Метод, следовательно, компилятор правильно сопоставляет его со статическим импортом.
Я не думаю, что это ошибка или что-то отличное от обычного импорта. Например, в случае обычного импорта, если у вас есть закрытый класс с тем же именем, что и импортированный, импортированный класс не будет отражен.