Почему вызов статического метода через экземпляр не является ошибкой для компилятора Java?

StackOverflow https://stackoverflow.com/questions/610458

  •  03-07-2019
  •  | 
  •  

Вопрос

Я уверен, что вы все знаете поведение, которое я имею в виду - такой код, как:

Thread thread = new Thread();
int activeCount = thread.activeCount();

вызывает предупреждение компилятора.Почему это не ошибка?

Редактировать:

Чтобы было ясно:вопрос не имеет никакого отношения к потокам.Я понимаю, что при обсуждении этого часто приводятся примеры потоков из-за возможности действительно все испортить с их помощью.Но на самом деле проблема в том, что такое использование всегда чушь, и вы не можете (грамотно) написать такой призыв и иметь это в виду.Любой пример вызова метода такого типа был бы сумасшедшим.Вот еще один:

String hello = "hello";
String number123AsString = hello.valueOf(123);

Это выглядит так, как будто каждый экземпляр String поставляется с методом "String valueOf(int i)".

Это было полезно?

Решение

В принципе, я считаю, что разработчики Java допустили ошибку, когда разрабатывали язык, и уже слишком поздно исправлять это из-за связанных с этим проблем совместимости.Да, это может привести к появлению очень вводящего в заблуждение кода.Да, вам следует избегать этого.Да, вы должны убедиться, что ваша IDE настроена на обработку этого как ошибки, IMO.Если вы когда-нибудь сами разработаете язык, имейте это в виду как пример того, чего следует избегать :)

Просто чтобы ответить на замечание DJClayworth, вот что разрешено в C#:

public class Foo
{
    public static void Bar()
    {
    }
}

public class Abc
{
    public void Test()
    {
        // Static methods in the same class and base classes
        // (and outer classes) are available, with no
        // qualification
        Def();

        // Static methods in other classes are available via
        // the class name
        Foo.Bar();

        Abc abc = new Abc();

        // This would *not* be legal. It being legal has no benefit,
        // and just allows misleading code
        // abc.Def();
    }

    public static void Def()
    {
    }
}

Почему я думаю, что это вводит в заблуждение?Потому что, если я посмотрю на код someVariable.SomeMethod() Я ожидаю, что это будет используйте значение someVariable.Если SomeMethod() это статический метод, это ожидание недопустимо;код обманывает меня.Как это вообще может быть хорошо вещь?

Как ни странно, Java не позволяет вам использовать потенциально неинициализированную переменную для вызова статического метода, несмотря на тот факт, что единственной информацией, которую она собирается использовать, является объявленный тип переменной.Это непоследовательный и бесполезный беспорядок.Зачем позволять это?

Редактировать:Эта правка является ответом на ответ Клейтона, в котором утверждается, что она допускает наследование статических методов.Это не так.Статические методы просто не являются полиморфными.Вот короткая, но полная программа, демонстрирующая это:

class Base
{
    static void foo()
    {
        System.out.println("Base.foo()");
    }
}

class Derived extends Base
{
    static void foo()
    {
        System.out.println("Derived.foo()");
    }
}

public class Test
{
    public static void main(String[] args)
    {
        Base b = new Derived();
        b.foo(); // Prints "Base.foo()"
        b = null;
        b.foo(); // Still prints "Base.foo()"
    }
}

Как вы можете видеть, значение времени выполнения b полностью игнорируется.

Другие советы

Почему это должно быть ошибкой?Экземпляр имеет доступ ко всем статическим методам.Статические методы не могут изменить состояние экземпляра (пытаясь является ошибка компиляции).

Проблема с хорошо известным примером, который вы приводите, очень специфична для Ветки, а не вызовы статических методов.Похоже, что вы начинаете понимать activeCount() для потока, на который ссылается thread, но вы действительно получаете количество для вызывающего потока.Это логическая ошибка, которую допускаете вы как программист.Выдача предупреждения - это то, что компилятор должен сделать в этом случае.Вы сами должны прислушаться к предупреждению и исправить свой код.

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

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

Я согласен с вами в том, что это должно быть ошибкой.Возможно, у компилятора должна быть опция / профиль для обновления некоторых предупреждений до ошибок.

Обновить: Когда они представили утверждать ключевое слово в версии 1.4, которое имеет аналогичные потенциальные проблемы совместимости со старым кодом, они сделали это доступно только в том случае, если вы явно установили исходный режим на "1.4"..Я полагаю, что можно было бы сделать это ошибкой в новом исходном режиме "java 7".Но я сомневаюсь, что они стали бы это делать, учитывая все те хлопоты, которые это вызвало бы.Как указывали другие, это не является строго необходимым для того, чтобы помешать вам писать запутанный код.И языковые изменения в Java должны быть ограничены строго необходимыми на данном этапе.

Короткий ответ - язык допускает это, так что это не ошибка.

Вероятно, по той же логике, которая делает это не ошибкой:

public class X
{
    public static void foo()
    {
    }

    public void bar()
    {
        foo(); // no need to do X.foo();
    }
}

Действительно важной вещью, с точки зрения компилятора, является то, что он способен разрешать символы.В случае статического метода ему необходимо знать, в каком классе его искать, поскольку он не связан с каким-либо конкретным объектом.Разработчики Java, очевидно, решили, что, поскольку они могут определить класс объекта, они также могут разрешить класс любого статического метода для этого объекта из любого экземпляра объекта.Они предпочитают допускать это - возможно, под влиянием наблюдения @TofuBeer - чтобы дать программисту некоторое удобство.Другие разработчики языков сделали другой выбор.Я, вероятно, попал бы в лагерь последних, но для меня это не имеет такого большого значения.Я, вероятно, разрешил бы использование, о котором упоминает @TofuBeer, но, разрешив это, моя позиция по запрету доступа из переменной экземпляра менее обоснована.

Это не ошибка, потому что это часть спецификации, но вы, очевидно, спрашиваете о логическом обосновании, о котором мы все можем догадаться.

Я предполагаю, что источник этого на самом деле заключается в том, чтобы позволить методу в классе вызывать статический метод в том же классе без лишних хлопот.Поскольку вызов x() является законным (даже без собственного имени класса), вызов this.x() также должен быть законным, и, следовательно, вызов через любой объект также был сделан законным.

Это также помогает побудить пользователей превращать частные функции в статические, если они не меняют состояние.

Кроме того, компиляторы обычно стараются избегать объявления ошибок, когда это никак не может привести к прямой ошибке.Поскольку статический метод не изменяет состояние и не заботится о вызывающем объекте, он не вызывает фактической ошибки (просто путаницы), чтобы разрешить это.Достаточно предупреждения.

Цель ссылки на переменную экземпляра состоит только в том, чтобы указать тип, который заключает в себе статику.Если вы посмотрите на байт-код, вызывающий статику через instance.staticMethod или EnclosingClass.staticMethod, создается тот же байт-код вызова статического метода.Ссылка на экземпляр не отображается.

Ответ также, почему это там есть, ну, он просто есть.До тех пор, пока вы используете этот класс.и не через инстанс, вы поможете избежать путаницы в будущем.

Вероятно, вы можете изменить это в своей IDE (в настройках Eclipse -> Java -> Компилятор -> Ошибки / Предупреждения)

Для этого нет другого выхода.В java (как и во многих других языках программирования) вы можете иметь доступ ко всем статическим членам класса через его имя класса или объект экземпляра этого класса.Это зависит от вас, вашего случая и программного решения, которое вы должны использовать, чтобы обеспечить большую читабельность.

Я просто обдумываю это:

instanceVar.staticMethod();

сокращенно для этого:

instanceVar.getClass().staticMethod();

Если бы тебе всегда приходилось это делать:

SomeClass.staticMethod();

тогда вы не смогли бы использовать наследование для статических методов.

То есть, вызывая статический метод через экземпляр, вам не нужно знать, к какому конкретному классу относится экземпляр во время компиляции, только то, что он реализует staticMethod() где-то в цепочке наследования.

Редактировать:Этот ответ неверен.Подробности смотрите в комментариях.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top