Статический импорт Java - PullRequest
26 голосов
/ 23 января 2012

Просто экспериментально я обнаружил, что нестатические методы 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 ]

24 голосов
/ 23 января 2012

Объяснение простое, хотя оно не меняет того факта, что поведение крайне неинтуитивно:

При разрешении метода, который должен быть вызван, первое, что делает компилятор, - находит наименьшую вмещающую область, которая имеетметод правильного имени.Только тогда появляются другие вещи, такие как разрешение перегрузки и совместная работа в игре.

Теперь, что здесь происходит, это то, что наименьшая включающая область действия, содержащая метод toString(), - это класс A, который наследует его от Object.Поэтому мы останавливаемся там и не ищем дальше.К сожалению, затем компилятор пытается найти наилучшее соответствие методов в данной области и замечает, что он не может вызвать ни один из них, и выдает ошибку.

Что означает никогда статический импортметоды с именем, идентичным методу в Object, потому что методы, которые естественным образом находятся в области видимости, имеют приоритет над статическим импортом (JLS подробно описывает теневое копирование метода, но я думаю, что для этой проблемы гораздо прощепросто запомните это).

Edit: @ alf любезно предоставил правую часть JLS, которая описывает вызов метода для тех, кто хочет получить полную картину.Это довольно сложно, но тогда проблема не простая, так что этого следовало ожидать.

4 голосов
/ 23 января 2012

Это не переопределение.Если бы это работало, this.toString() все равно получал бы доступ к методу A вместо Arrays.toString, как это было бы в случае переопределения.

Спецификация языка объясняет, что статическийимпорт влияет только на разрешение static методов и типов:

Одно-статическое объявление импорта d в ​​модуле компиляции c пакета p, которое импортирует поле с именем n, затеняет объявление любого статическогополе с именем n, импортируемое объявлением static-import-on-demand в c, по всему c.

Одно-статическое объявление импорта d в ​​модуле компиляции c пакета p, который импортирует метод с именем n с подписьюs скрывает объявление любого статического метода с именем n с подписью s, импортированного объявлением static-import-on-demand в c, по всему c.

Объявление одиночного статического импорта dв модуле компиляции c пакета p, который импортирует тип с именем n, затеняет объявления:

  • any статический тип с именем n imported объявлением статического импорта по требованию в c.
  • any тип верхнего уровня (§7.6) с именем n, объявленным в другом модуле компиляции (§7.3) из p.
  • любой тип с именем n , импортированный с помощью объявления типа импорта по требованию (§7.5.2) в c.в течение c.

Статический импорт не скрывает нестатические методы или внутренние типы.

Таким образом, toString не скрывает нестатический метод.Поскольку имя toString может ссылаться на нестатический метод A, оно не может ссылаться на static метод Arrays и, таким образом, toString связывается с единственным доступным методом с именем toString, который доступенв объеме, который составляет String toString().Этот метод не может принимать аргументы, поэтому вы получаете ошибку компиляции.

В разделе 15.12.1 поясняется разрешение метода, и его пришлось бы полностью переписать, чтобы разрешить дублирование недоступных имён методов внутри static методов, но не внутри member методов.

Я предполагаю, что разработчики языка хотели сохранить простые правила разрешения методов, что означает, что одно и то же имя означает одно и то же, независимо от того, появляется оно в методе static или нет, и единственное, что можно изменить, - это то, что доступно.

1 голос
/ 23 января 2012

Если вы попытаетесь выполнить похожий код, то вы не получите сообщение об ошибке компилятора

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[]) метода, следовательно, компилятор правильно сопоставляет его со статическим импортом .

0 голосов
/ 23 января 2012

Я не думаю, что это ошибка или что-то отличное от обычного импорта.Например, в случае обычного импорта, если у вас есть закрытый класс с тем же именем, что и импортированный, импортированный класс не будет отражен.

...