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

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

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

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

EDIT:

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

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

Что делает его похожим на то, что каждый экземпляр String имеет метод String valueOf (int i).

Ответ 1

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

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

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 не позволит вам использовать потенциально неинициализированную переменную для вызова статического метода, несмотря на то, что единственной информацией, которую он будет использовать, является объявленный тип переменной. Это непоследовательный и бесполезный беспорядок. Зачем это нужно?

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

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 полностью игнорируется.

Ответ 2

Потому что спецификация языка позволяет это:) Как и VB, и С# не делает.

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

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

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

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

Ответ 7

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

Ответ 8

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

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

Ответ 9

Возможно, вы можете изменить его в своей среде IDE (в Eclipse Preferences → Java → Compiler → Errors/Warnings)

Ответ 10

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

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

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

Ответ 11

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

Ответ 12

Я просто считаю это:

instanceVar.staticMethod();

чтобы быть сокращенными для этого:

instanceVar.getClass().staticMethod();

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

SomeClass.staticMethod();

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

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

EDIT: этот ответ неверен. Подробнее см. Комментарии.