Можем ли мы переопределить статические методы? Почему выход так? [Дубликат]

Проверьте , как использовать «update-alternatives» , чтобы обойти эту проблему:

... Если вы установите gcc 4.6, вы также можете использовать обновление -alternatives, чтобы легко переключаться между версиями. Это можно настроить с помощью:

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-4.6 
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.7 40 --slave /usr/bin/g++ g++ /usr/bin/g++-4.7 
sudo update-alternatives --config gcc
blockquote>

460
задан Michael Myers 8 February 2010 в 18:14
поделиться

21 ответ

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

Было высказано два соображения, которые влияли на дизайн Java, который повлиял на это. Один из них был проблемой производительности: было много критики Smalltalk о том, что он слишком медленный (сбор мусора и полиморфные звонки являются частью этого), и создатели Java были полны решимости избежать этого. Другим было решение о том, что целевой аудиторией Java являются разработчики на C ++. Статические методы работают так, как они это делают, и они были полезны для программистов на С ++, а также были очень быстрыми, потому что нет необходимости ждать, пока время выполнения не выяснит, какой метод вызывать.

437
ответ дан Nathan Hughes 18 August 2018 в 21:24
поделиться
  • 1
    ... но только "правильный" в Java. Например, эквивалент Scala "статических классов" (которые называются objects) позволяют перегружать методы. – user 4 November 2011 в 08:25
  • 2
    Objective-C также позволяет переопределять методы класса . – Richard 9 January 2012 в 19:04
  • 3
    Существует иерархия типа времени компиляции и иерархия типов времени выполнения. Имеет смысл спросить, почему вызов статического метода не использует иерархию типов времени выполнения в тех случаях, когда она существует. В Java это происходит при вызове статического метода из объекта (obj.staticMethod()) - который разрешен и использует типы времени компиляции. Когда статический вызов находится в нестационарном методе класса, то «текущий» объект может быть производным типом класса, но статические методы, определенные для производных типов, не рассматриваются (они находятся в иерархии типов выполнения). – Steve Powell 10 October 2012 в 14:43
  • 4
    Я должен был прояснить это: not true, что понятие не применимо . – Steve Powell 10 October 2012 в 14:55
  • 5
    Этот ответ, хотя и правильный, более схож с «тем, как он». а не как это должно быть или более точно, как это могло бы быть, чтобы оправдать ожидания ОП и, следовательно, здесь, я и другие. Нет никакой конкретной причины, чтобы запретить переопределение статических методов, отличных от «вот как это». Я думаю, что это недостаток лично. – RichieHH 11 May 2014 в 19:19

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

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

Пример, см. ниже код: -

public class StaticMethodsHiding {
    public static void main(String[] args) {
        SubClass.hello();
    }
}


class SuperClass {
    static void hello(){
        System.out.println("SuperClass saying Hello");
    }
}


class SubClass extends SuperClass {
    // static void hello() {
    // System.out.println("SubClass Hello");
    // }
}

Выход: -

SuperClass saying Hello

См. Java oracle docs и поиск Что вы можете сделать в подклассе для получения дополнительной информации о скрытие статических методов в подклассе.

Спасибо

-1
ответ дан Aman 18 August 2018 в 21:24
поделиться

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

4
ответ дан Athens Holloway 18 August 2018 в 21:24
поделиться
  • 1
    Разве вы не читали какой-либо другой ответ, уже охватывающий это, и ясно, что это не достаточные причины, чтобы исключить переоценку статики на концептуальном уровне. Мы знаем, что это не работает. Совершенно «чисто», чтобы желать переопределения статических методов, и действительно это возможно на многих других языках. – RichieHH 11 May 2014 в 19:24
  • 2
    Ричард, давайте предположим на минуту, что, когда я ответил на этот вопрос 4 года назад, большинство из этих ответов не было опубликовано, так что не будьте задницей! Нет необходимости утверждать, что я не читал внимательно. Кроме того, вы не читали, что мы обсуждаем только переопределение по отношению к Java. Кто заботится о том, что возможно на других языках. Это не имеет значения. Иди тролль куда-нибудь еще. Ваш комментарий не добавляет ничего полезного в этот поток. – Athens Holloway 12 May 2014 в 15:47

При переопределении мы можем создать полиморфный характер в зависимости от типа объекта. Статический метод не имеет отношения к объекту. Поэтому java не может поддерживать переопределение статического метода.

3
ответ дан DeveloperArnab 18 August 2018 в 21:24
поделиться

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

2
ответ дан G4uKu3_Gaurav 18 August 2018 в 21:24
поделиться
  • 1
    – DJClayworth 29 June 2015 в 20:09
  • 2
    @DJClayworth вы должны следовать этой ссылке для подробного ответа geeksforgeeks.org/… – G4uKu3_Gaurav 29 June 2015 в 20:29
  • 3
    Спасибо за ссылку. На самом деле я здесь, чтобы быть полезным для новичков на сайте, чтобы объяснить, как сайт работает для тех, кто не привык к нему, а не потому, что мне нужен был ответ на вопрос. – DJClayworth 29 June 2015 в 21:19

Лично я считаю, что это недостаток в дизайне Java. Да, да, я понимаю, что нестатические методы привязаны к экземпляру, в то время как статические методы привязаны к классу и т. Д. И т. Д. Однако рассмотрим следующий код:

public class RegularEmployee {
    private BigDecimal salary;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }

    public BigDecimal calculateBonus() {
        return salary.multiply(getBonusMultiplier());
    }

    /* ... presumably lots of other code ... */
}

public class SpecialEmployee extends RegularEmployee {
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

Этот код не будет работать как и следовало ожидать. А именно, SpecialEmployee получает бонус в размере 2%, как и обычные сотрудники. Но если вы удалите «статические», то SpecialEmployee получит 3% бонус.

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

Мне кажется вполне правдоподобным, что вы можете захотеть чтобы сделать getBonusMultiplier статическим. Возможно, вы хотите показать мультипликатор бонуса для всех категорий сотрудников без необходимости иметь экземпляр сотрудника в каждой категории. Какой смысл искать такие примеры? Что делать, если мы создаем новую категорию сотрудников и пока не имеем каких-либо сотрудников, назначенных ей? Это логически статическая функция.

Но это не сработает.

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

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

Или, может быть, есть веская причина, почему Java ведет себя таким образом. Может ли кто-нибудь указать на преимущество этого поведения, какую-то категорию проблемы, которая облегчается этим? Я имею в виду, не просто указывайте мне на спецификацию языка Java и скажите «см., Это документировано, как оно себя ведет». Я знаю это. Но есть ли веская причина, почему ему так надо? (Кроме того, очевидное «заставить работать правильно было слишком сложно» ...)

Обновить

@VicKirk: Если вы имеете в виду, что это «плохой дизайн», как Java обрабатывает статику, мой ответ: «Ну, ну, конечно». Как я сказал в своем оригинальном посте, это не сработает. Но если вы имеете в виду, что это плохой дизайн в том смысле, что в нем будет что-то принципиально неправильное с языком, где это сработало, то есть, где статика может быть переопределена, как и виртуальные функции, что это каким-то образом приведет к двусмысленности, или было бы невозможно эффективно или что-то подобное, я отвечаю: «Почему? Что не так с концепцией?»

Я думаю, что пример, который я даю, - это очень естественная вещь, которую нужно делать. У меня есть класс, который имеет функцию, которая не зависит от каких-либо данных экземпляра и которую я могу очень разумно вызывать независимо от экземпляра, а также желать вызвать из метода экземпляра. Почему это не работает? На протяжении многих лет я сталкивался с этой ситуацией довольно много. На практике я обхожу его, создавая функцию virtual, а затем создаю статический метод, единственной целью которого является статический метод, который передает вызов виртуальному методу с фиктивным экземпляром. Это похоже на очень окольный путь.

164
ответ дан Jay 18 August 2018 в 21:24
поделиться
  • 1
    @Bemrose: Но это моя мысль: почему мне не разрешено это делать? Возможно, моя интуитивная концепция того, что такое «статический» должен отличаться от вашего, но в основном я думаю о статике как методе, который может быть статичным, потому что он не использует данные экземпляра и который ДОЛЖЕН быть статичным, потому что вы можете назвать его независимо от экземпляра. Статичность явно привязана к классу: я ожидаю, что Integer.valueOf будет привязан к целым и Double.valueOf для привязки к парным разрядам. – Jay 8 February 2010 в 21:12
  • 2
    @ewernli & amp; Бемроуз: Да, так оно и есть. Я не обсуждаю это. Поскольку код в моем примере не работает, конечно, я бы не стал его писать. Мой вопрос в том, почему так. (Я боюсь, что это превращается в один из тех разговоров, где мы просто не общаемся. «Извините меня, мистер Коммандер, могу ли я получить один из них красным?» «Нет, это стоит 5 долларов» «Да» , Я знаю, что это стоит 5 долларов, но могу ли я получить красный? »« Сэр, я просто сказал вам, что он стоит 5 долларов »« Хорошо, я знаю цену, но я спрашивал о цвете. «Я уже сказал вам цену!» и т. д.), – Jay 8 February 2010 в 21:29
  • 3
    Я думаю, что в конечном итоге этот код запутан. Рассмотрим, передается ли экземпляр в качестве параметра. Затем вы говорите, что экземпляр среды выполнения должен определять, какой статический метод вызывается. Это в основном делает целую отдельную иерархию параллельной существующему экземпляру. Теперь, что, если подкласс определяет ту же самую сигнатуру метода, что и нестатический? Я думаю, что правила сделают вещи довольно сложными. Это именно такие языковые сложности, которые Java пытается избежать. – Yishai 8 February 2010 в 23:24
  • 4
    PS Я не хочу бесконечно обходить аргумент. Если мы не согласны, в какой-то момент мы должны просто сказать: «Хорошо, мы не согласны и не собираемся убеждать другого». Но если вы двое хотите продолжать комментировать комментарии, я играю. – Jay 10 February 2010 в 16:11
  • 5
    Я думаю, что у Джей есть точка - это тоже меня удивило, когда я обнаружил, что статика не может быть переопределена. Отчасти потому, что если у меня есть A с методом someStatic() и B расширяет A, то B.someMethod() связывает с методом в A. Если впоследствии я добавлю someStatic() в B, вызывающий код все еще вызывает A.someStatic(), пока я не перекомпилирую код вызова. Также он удивил меня тем, что bInstance.someStatic() использует тип объявил тип bInstance, а не тип среды выполнения, потому что он связывается при компиляции не с ссылкой, поэтому A bInstance; ... bInstance.someStatic() вызывает A.someStatic (), если если B.someStatic ( ) существует. – Lawrence Dol 25 February 2010 в 19:46

Переопределение метода стало возможным благодаря динамической диспетчеризации , что означает, что объявленный тип объекта не определяет его поведение, а скорее его тип времени выполнения:

Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"

Несмотря на то, что оба lassie и kermit объявлены как объекты типа Animal, их поведение (метод .speak()) изменяется, потому что динамическая диспетчеризация будет только связывать вызов метода .speak() с реализация во время выполнения - не во время компиляции.

Теперь, вот где ключевое слово static начинает иметь смысл: слово «статический» является антонимом для «динамического». Поэтому причина, по которой вы не можете переопределить статические методы, заключается в том, что динамические диспетчеризации для статических элементов нет, потому что статическое буквально означает «не динамический». Если они отправлены динамически (и, следовательно, могут быть переопределены), ключевое слово static просто больше не имеет смысла.

10
ответ дан Jayanga Kaushalya 18 August 2018 в 21:24
поделиться

В Java (и многих языках ООП, но я не могу говорить за всех, а некоторые вообще не имеют статичности), все методы имеют фиксированную подпись - параметры и типы. В виртуальном методе подразумевается первый параметр: ссылка на сам объект и при вызове внутри объекта, компилятор автоматически добавляет this.

Нет никакой разницы для статических методов - они все еще имеют фиксированную подпись. Однако, объявив метод static, вы прямо заявили, что компилятор не должен включать параметр подразумеваемого объекта в начале этой подписи. Поэтому любой другой код, который вызывает это, должен не должен пытаться помещать ссылку на объект в стеке . Если это так, то выполнение метода не будет работать, поскольку параметры будут не в том месте, которые сдвинуты на один - в стеке.

Из-за этой разницы между ними; виртуальные методы всегда имеют ссылку на объект контекста (т. е. this), поэтому можно ссылаться на что-либо внутри кучи, принадлежащей этому экземпляру объекта. Но со статическими методами, поскольку эта ссылка не передана, этот метод не может получить доступ к каким-либо объектным переменным и методам, поскольку контекст неизвестен.

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

Как кто-то спросил в комментарии к op - какова ваша причина и цель для желания этой функции?

Я не знаю Ruby много, как это было упомянуто OP, Я провел некоторое исследование. Я вижу, что в классах Ruby действительно особый вид объекта, и можно создавать (даже динамически) новые методы. Классы - это объекты полного класса в Ruby, они не находятся на Java. Это то, что вам нужно будет принять при работе с Java (или C #). Это не динамические языки, хотя C # добавляет некоторые формы динамических. На самом деле Ruby не имеет «статических» методов, насколько я мог найти - в этом случае это методы для объекта singleton class. Затем вы можете переопределить этот синглтон с новым классом, а методы в предыдущем объекте класса вызовут те, которые определены в новом классе (правильно?). Поэтому, если вы вызвали метод в контексте исходного класса, он все равно выполнил бы только исходную статику, но вызов метода в производном классе вызовет методы либо из родительского, либо подкласса. Интересно, и я вижу в этом какую-то ценность. Требуется другой шаблон мысли.

Поскольку вы работаете на Java, вам нужно будет приспособиться к этому. Почему они это сделали? Ну, возможно, чтобы улучшить производительность в то время на основе технологии и понимания, которые были доступны. Компьютерные языки постоянно развиваются. Вернитесь достаточно далеко, и нет такой вещи, как ООП. В будущем появятся другие новые идеи.

EDIT: Еще один комментарий. Теперь, когда я вижу различия и как сам разработчик Java / C #, я могу понять, почему ответы, которые вы получаете от разработчиков Java, могут сбивать с толку, если вы исходите с такого языка, как Ruby. Методы Java static не совпадают с методами Ruby class. Разработчикам Java будет трудно понять это, так же как и те, кто работает в основном с языком Ruby / Smalltalk. Я вижу, как это также будет очень смущать тем фактом, что Java также использует метод класса, как другой способ говорить о статических методах, но этот же термин используется по-разному Ruby. Java не имеет методов класса стиля Ruby (извините); Ruby не имеет статических методов стиля Java, которые на самом деле являются просто старыми процедурными функциями стиля, как показано на C.

Кстати - спасибо за вопрос! Я узнал что-то новое для меня сегодня о методах класса (стиль Ruby).

5
ответ дан Kevin Brock 18 August 2018 в 21:24
поделиться
  • 1
    Разумеется, нет причин, по которым Java не может пройти «Класс», объект как скрытый параметр для статических методов. Он просто не был предназначен для этого. – jmucchiello 23 June 2010 в 17:58

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

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

Динамическое полиморфное поведение возникает, когда программист использует объект и получает доступ к методу экземпляра. JRE будет отображать различные методы экземпляра разных классов в зависимости от того, какой тип объекта вы используете.

Когда вы говорите, что переопределяете статические методы, статические методы, к которым мы будем обращаться, используя имя класса, которое будет связано с компиляцией времени, поэтому во время работы с статическими методами нет концепции связывания методов. Таким образом, термин «переопределяющий» статические методы сам по себе не имеет никакого значения.

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

7
ответ дан KIN 18 August 2018 в 21:24
поделиться
  • 1
    Это не так во многих случаях, когда во время выполнения статический метод фактически вызывается из экземпляра содержащего класса, и поэтому вполне возможно определить, какие экземпляры функции вызывать. – RichieHH 11 May 2014 в 19:26
  • 2
    Как противопоставить интуитивно иметь public abstract IBox createBox(); внутри интерфейса IBox? Ящик может реализовать IBox, чтобы переопределить createBox, и создать объект result в действительном IBox, иначе верните null. Конструкторы не могут возвращать "null" и поэтому вы вынуждены (1) использовать исключения ВЕЗДЕ (что мы делаем сейчас) или (2) создавать фабричные классы, которые делают то, что я сказал ранее, но таким образом, что нет смысла для новичков или экспертов Java (которые мы также Делай сейчас). Статические нереализованные методы решают эту проблему. – Dmitry 5 September 2016 в 21:52
  • 3
    static не означает время компиляции, статическое означает, что оно привязано к классу, а не к любому конкретному объекту. он делает так много, если не больше смысла, чем создание фабрики классов, за исключением того, что static Box.createBox имеет больше смысла, чем BoxFactory.createBox, и является неизбежным шаблоном при необходимости проверки конструкции ошибки без исключения исключений (конструкторы не могут потерпеть неудачу, они могут только убить исключение process / throw), тогда как статический метод может возвращать значение null при ошибке или даже принимать обратные вызовы с ошибками / ошибками, чтобы написать что-то вроде hastebin.com/codajahati.java . – Dmitry 5 September 2016 в 22:34

В общем случае не имеет смысла позволять «переопределять» статические методы, поскольку не было бы хорошего способа определить, какой вызов вызывать во время выполнения. Взяв пример Employee, если мы вызываем RegularEmployee.getBonusMultiplier () - какой метод должен быть выполнен?

В случае Java можно представить себе определение языка, где можно «переопределить» статическую методы, если они вызываются через экземпляр объекта. Однако все это будет заключаться в повторной реализации обычных методов класса, добавляя избыточность к языку, не добавляя при этом никакой пользы.

4
ответ дан Lars 18 August 2018 в 21:24
поделиться
  • 1
    Интуитивно я бы подумал, что он должен работать так же, как виртуальная функция. Если B расширяет A, и оба A и B имеют виртуальные функции с именем doStuff, компилятор знает, что экземпляры A должны использовать A.doStuff, а экземпляры B должны использовать B.doStuff. Почему он не может сделать то же самое со статическими функциями? В конце концов, компилятор знает, к какому классу относится каждый объект. – Jay 8 February 2010 в 21:35
  • 2
    Erm ... Jay, статический метод не обязательно должен быть (вообще не нужен), вызываемый экземпляром ... – meriton 8 February 2010 в 23:06
  • 3
    @meriton, но тогда это еще проще, нет? Если статический метод вызывается с использованием имени класса, вы должны использовать метод, соответствующий классу. – CPerkins 8 February 2010 в 23:37
  • 4
    Но тогда для вас главное. Если вы вызываете A.doStuff (), если он использует версию, которая была переопределена в & quot; B extends A & quot; или версия, которая была переопределена в «C extends A». И если у вас есть C или B, то вы все равно называете эти версии ... не нужно переопределять. – PSpeed 9 February 2010 в 09:01
  • 5
    @meriton: Это правда, что статический метод обычно не вызывается с экземпляром, но я предполагаю, что это потому, что такой вызов не приносит ничего полезного, учитывая текущий дизайн Java! Я предполагаю, что альтернативный дизайн мог быть лучшей идеей. BTW, в очень реальном смысле статические функции вызываются с экземпляром довольно регулярно: когда вы вызываете статическую функцию из виртуальной функции. Затем вы неявно получаете this.function (), то есть текущий экземпляр. – Jay 25 February 2010 в 22:34

Статические методы рассматриваются как глобальные JVM, они вообще не связаны с экземпляром объекта.

Это может быть концептуально возможно, если вы могли бы вызвать статические методы из объектов класса (например, на таких языках, как Smalltalk), но это не так на Java.

EDIT

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

class MyClass { ... }
class MySubClass extends MyClass { ... }

MyClass obj1 = new MyClass();
MySubClass obj2 = new MySubClass();

ob2 instanceof MyClass --> true

Class clazz1 = obj1.getClass();
Class clazz2 = obj2.getClass();

clazz2 instanceof clazz1 --> false

Вы можете размышлять над классами, но он останавливается там. Вы не вызываете статический метод, используя clazz1.staticMethod(), но используя MyClass.staticMethod(). Статический метод не привязан к объекту и, следовательно, не существует понятия this или super в статическом методе. Статический метод - глобальная функция; как следствие, также нет понятия полиморфизма, и поэтому переопределение метода не имеет смысла.

Но это возможно, если MyClass был объектом во время выполнения, на котором вы вызываете метод, как в Smalltalk (или, может быть, JRuby, как один комментарий, но я ничего не знаю о JRuby).

О да ... еще одна вещь. Вы можете вызвать статический метод через объект obj1.staticMethod(), но это действительно синтаксический сахар для MyClass.staticMethod(), и его следует избегать. Обычно это вызывает предупреждение в современной среде IDE. Я не знаю, почему они когда-либо допускали этот ярлык.

16
ответ дан Lawrence Dol 18 August 2018 в 21:24
поделиться
  • 1
    Даже многие современные языки, такие как Ruby, имеют класс-методы и позволяют переопределять их. – Chandra Sekar S 8 February 2010 в 18:23
  • 2
    Классы существуют как объекты в Java. См. Раздел «Класс». класс. Я могу сказать myObject.getClass (), и он вернет мне экземпляр соответствующего объекта класса. – Jay 8 February 2010 в 19:16
  • 3
    Вы получаете только «описание». класса, а не самого класса. Но разница тонкая. – ewernli 11 February 2010 в 17:33
  • 4
    У вас все еще есть класс, но он скрыт в виртуальной машине (около загрузчика классов), у пользователя почти нет доступа к нему. – mathk 23 June 2010 в 17:10
  • 5
    Чтобы правильно использовать clazz2 instanceof clazz1, вы можете вместо этого использовать class2.isAssignableFrom(clazz1), который, я считаю, вернет true в вашем примере. – Simon Forsberg 28 June 2013 в 12:31

На самом деле мы ошибались. Несмотря на то, что Java по умолчанию не позволяет переопределять статические методы, если вы тщательно изучаете документацию классов класса и метода в Java, вы все равно можете найти способ эмуляции статических методов, переопределяющих следующие методы:

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;

class RegularEmployee {

    private BigDecimal salary = BigDecimal.ONE;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }
    public BigDecimal calculateBonus() {
        return salary.multiply(this.getBonusMultiplier());
    }
    public BigDecimal calculateOverridenBonus() {
        try {
            // System.out.println(this.getClass().getDeclaredMethod(
            // "getBonusMultiplier").toString());
            try {
                return salary.multiply((BigDecimal) this.getClass()
                    .getDeclaredMethod("getBonusMultiplier").invoke(this));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
    // ... presumably lots of other code ...
}

final class SpecialEmployee extends RegularEmployee {

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

public class StaticTestCoolMain {

    static public void main(String[] args) {
        RegularEmployee Alan = new RegularEmployee();
        System.out.println(Alan.calculateBonus());
        System.out.println(Alan.calculateOverridenBonus());
        SpecialEmployee Bob = new SpecialEmployee();
        System.out.println(Bob.calculateBonus());
        System.out.println(Bob.calculateOverridenBonus());
    }
}

Результат:

0.02
0.02
0.02
0.03

, чего мы пытались достичь:)

Даже если мы объявим третью переменную Carl как RegularEmployee и назначим ей экземпляр SpecialEmployee, мы будем все еще имеют вызов метода RegularEmployee в первом случае и вызов метода SpecialEmployee во втором случае

RegularEmployee Carl = new SpecialEmployee();

System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());

просто посмотрите на консоль вывода:

0.02
0.03

;)

22
ответ дан Mr_and_Mrs_D 18 August 2018 в 21:24
поделиться
  • 1
    Да, отражение - это почти единственное, что можно сделать, - но вопрос не совсем в этом - полезно иметь его здесь, хотя – Mr_and_Mrs_D 29 November 2013 в 21:29
  • 2
    Хороший взлом, но маловероятно, чтобы кто-то использовал это в фактическом производстве .... – Pacerier 10 September 2014 в 16:54
  • 3
    Этот ответ - самый большой хак, который я видел до сих пор по всем темам Java. Было весело читать его еще :) – Andrejs 19 March 2017 в 14:42

Следующий код показывает, что это возможно:

class OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriden Meth");   
}   

}   

public class OverrideStaticMeth extends OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriding Meth");   
}   

public static void main(String[] args) {   
OverridenStaticMeth osm = new OverrideStaticMeth();   
osm.printValue();   

System.out.println("now, from main");
printValue();

}   

} 
-3
ответ дан pop stack 18 August 2018 в 21:24
поделиться
  • 1
    Нет, нет; статический объявленный тип osm - OverridenStaticMeth не OverrideStaticMeth. – Lawrence Dol 10 September 2014 в 20:24
  • 2
    Кроме того, я постараюсь избежать использования так много Meth во время программирования & lt; big grin & gt ;; – Lawrence Dol 10 September 2014 в 20:25

Простое решение: используйте экземпляр singleton. Это позволит переопределить и наследовать.

В моей системе у меня есть класс SingletonsRegistry, который возвращает экземпляр для переданного класса. Если экземпляр не найден, он создается.

Класс языка Haxe:

package rflib.common.utils;
import haxe.ds.ObjectMap;



class SingletonsRegistry
{
  public static var instances:Map<Class<Dynamic>, Dynamic>;

  static function __init__()
  {
    StaticsInitializer.addCallback(SingletonsRegistry, function()
    {
      instances = null;
    });

  } 

  public static function getInstance(cls:Class<Dynamic>, ?args:Array<Dynamic>)
  {
    if (instances == null) {
      instances = untyped new ObjectMap<Dynamic, Dynamic>();      
    }

    if (!instances.exists(cls)) 
    {
      if (args == null) args = [];
      instances.set(cls, Type.createInstance(cls, args));
    }

    return instances.get(cls);
  }


  public static function validate(inst:Dynamic, cls:Class<Dynamic>)
  {
    if (instances == null) return;

    var inst2 = instances[cls];
    if (inst2 != null && inst != inst2) throw "Can\'t create multiple instances of " + Type.getClassName(cls) + " - it's singleton!";
  }

}
1
ответ дан Raivo Fishmeister 18 August 2018 в 21:24
поделиться
  • 1
    Очень круто, это первый раз, когда я слышал о языке программирования Haxe :) – cyc115 6 August 2014 в 02:08
  • 2
    Это намного лучше реализовано в Java как статический метод для самого класса; Singleton.get(). Реестр является просто исчерпывающим надбавкой, и это исключает GC для классов. – Lawrence Dol 10 September 2014 в 20:21
  • 3
    Вы правы, это классическое решение. Я точно не помню, почему я выбрал реестр, вероятно, имел некоторые рамки мышления, которые привели к такому результату. – Raivo Fishmeister 11 September 2014 в 00:20

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

Метод в Java используется для отображения поведения объекта / класса. Здесь, поскольку метод статичен (т. Е. Статический метод используется для представления поведения только класса). Изменение / переопределение поведения всего класса нарушит явление одного из фундаментальных элементов объектно-ориентированного программирования, т. Е. Высокой степени сцепления , (помните, что конструктор - это особый вид метода в Java.)

High Cohesion. Один класс должен иметь только одну роль. Например: класс автомобиля должен производить только автомобильные объекты, а не велосипеды, грузовики, самолеты и т. Д. Но класс Car может иметь некоторые особенности (поведение), которые принадлежат только ему.

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


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

public class Vehicle {
static int VIN;

public static int getVehileNumber() {
    return VIN;
}}

class Car extends Vehicle {
static int carNumber;

public static int getVehileNumber() {
    return carNumber;
}}

Это потому, что здесь мы не переопределяем метод но мы просто повторно заявляем об этом. Java позволяет повторно объявить метод (статический / нестатический).

Удаление статического ключевого слова из метода getVehileNumber () класса Car приведет к ошибке компиляции. Поскольку мы пытаемся изменить функциональность статического метода, который принадлежит только классу Vehicle.

Кроме того, если getVehileNumber () объявлен как final, тогда код не будет компилироваться, поскольку последнее ключевое слово ограничивает программиста повторным объявлением метода.

public static final int getVehileNumber() {
return VIN;     }

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

1
ответ дан Rohit Gaikwad 18 August 2018 в 21:24
поделиться

Ну ... ответ НЕТ, если вы думаете с точки зрения того, как должен работать метод overriden в Java. Но вы не получаете никакой ошибки компилятора, если пытаетесь переопределить статический метод. Это означает, что если вы попытаетесь переопределить, Java не остановит вас; но вы, конечно, не получаете того же эффекта, что и для нестатических методов. Переопределение на Java просто означает, что конкретный метод будет вызываться в зависимости от типа времени выполнения объекта, а не от его типа времени компиляции (что имеет место при переопределении статических методов). Хорошо ... любые догадки по причине, почему они ведут себя странно? Поскольку они являются методами класса и, следовательно, доступ к ним всегда разрешается во время компиляции только с использованием информации типа времени компиляции. Доступ к ним с использованием ссылок на объекты - это дополнительная свобода, предоставляемая разработчиками Java, и мы не должны думать о том, чтобы остановить эту практику только тогда, когда они ее ограничивают: -)

Пример : давайте попробуем посмотреть, что произойдет, если мы попробуем переопределить статический метод: -

class SuperClass {
// ......
public static void staticMethod() {
    System.out.println("SuperClass: inside staticMethod");
}
// ......
}

public class SubClass extends SuperClass {
// ......
// overriding the static method
public static void staticMethod() {
    System.out.println("SubClass: inside staticMethod");
}

// ......
public static void main(String[] args) {
    // ......
    SuperClass superClassWithSuperCons = new SuperClass();
    SuperClass superClassWithSubCons = new SubClass();
    SubClass subClassWithSubCons = new SubClass();

    superClassWithSuperCons.staticMethod();
    superClassWithSubCons.staticMethod();
    subClassWithSubCons.staticMethod();
    // ...
}
}

Выход : - SuperClass: inside staticMethod SuperClass: inside staticMethod SubClass: inside staticMethod

Обратите внимание на вторую строку вывода. Если бы staticMethod был переопределен, эта строка должна была бы быть идентичной третьей строке, поскольку мы вызываем «staticMethod ()» на объект Runtime Type как «SubClass», а не как «SuperClass». Это подтверждает, что статические методы всегда разрешаются с использованием только информации типа времени компиляции.

5
ответ дан Rupesh Yadav 18 August 2018 в 21:24
поделиться

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

MyClass.static1()
MySubClass.static1()   // If you overrode, you have to call it through MySubClass anyway.

EDIT: Похоже, что через неудачный надзор в дизайне языка вы можете вызвать статические методы через экземпляр. Как правило, никто этого не делает. Мой плохой.

1
ответ дан Sam Harwell 18 August 2018 в 21:24
поделиться
  • 1
    «Вы не можете вызывать статические методы через экземпляр». На самом деле, один из причуд Java - это то, что вы можете называть статические методы через экземпляр, хотя это очень плохая идея. – Powerlord 8 February 2010 в 19:17
  • 2
    Действительно, Java позволяет доступ к статическим членам через экземпляры: см. Статическая переменная в Java – Richard JP Le Guen 20 September 2012 в 06:24
  • 3
    Соответствующая современная IDE генерирует предупреждение, когда вы это делаете, поэтому, по крайней мере, вы можете поймать его, в то время как Oracle может сохранить обратную совместимость. – Gimby 4 January 2013 в 11:24
  • 4
    Нет ничего принципиально неправильного в том, что вы можете вызвать статический метод через экземпляр. Это придирки для садистов. Почему что-то вроде экземпляра Date НЕ вызывают собственные статические методы, передавая данные экземпляра функции через вызывающий интерфейс? – RichieHH 11 May 2014 в 19:30

Короткий ответ: это вполне возможно, но Java этого не делает.

Вот какой код, который иллюстрирует текущее состояние дел дел в Java:

Файл Base.java:

package sp.trial;
public class Base {
  static void printValue() {
    System.out.println("  Called static Base method.");
  }
  void nonStatPrintValue() {
    System.out.println("  Called non-static Base method.");
  }
  void nonLocalIndirectStatMethod() {
    System.out.println("  Non-static calls overridden(?) static:");
    System.out.print("  ");
    this.printValue();
  }
}

Файл Child.java:

package sp.trial;
public class Child extends Base {
  static void printValue() {
    System.out.println("  Called static Child method.");
  }
  void nonStatPrintValue() {
    System.out.println("  Called non-static Child method.");
  }
  void localIndirectStatMethod() {
    System.out.println("  Non-static calls own static:");
    System.out.print("  ");
    printValue();
  }
  public static void main(String[] args) {
    System.out.println("Object: static type Base; runtime type Child:");
    Base base = new Child();
    base.printValue();
    base.nonStatPrintValue();
    System.out.println("Object: static type Child; runtime type Child:");
    Child child = new Child();
    child.printValue();
    child.nonStatPrintValue();
    System.out.println("Class: Child static call:");
    Child.printValue();
    System.out.println("Class: Base static call:");
    Base.printValue();
    System.out.println("Object: static/runtime type Child -- call static from non-static method of Child:");
    child.localIndirectStatMethod();
    System.out.println("Object: static/runtime type Child -- call static from non-static method of Base:");
    child.nonLocalIndirectStatMethod();
  }
}

Если вы запустите это (я сделал это на Mac, от Eclipse , используя Java 1.6), вы получаете:

Object: static type Base; runtime type Child.
  Called static Base method.
  Called non-static Child method.
Object: static type Child; runtime type Child.
  Called static Child method.
  Called non-static Child method.
Class: Child static call.
  Called static Child method.
Class: Base static call.
  Called static Base method.
Object: static/runtime type Child -- call static from non-static method of Child.
  Non-static calls own static.
    Called static Child method.
Object: static/runtime type Child -- call static from non-static method of Base.
  Non-static calls overridden(?) static.
    Called static Base method.

Здесь случаи only , которые могут быть сюрпризом (и о котором идет речь), выглядят как первый случай :

«Тип времени выполнения не используется для определения того, какие статические методы вызывают, даже если вызывается с экземпляром объекта (obj.staticMethod())».

и последний случай last :

«При вызове статического метода из объектного метода класса выбранный статический метод является единственным, доступным из самого класса, и не из класса, определяющего тип времени выполнения объекта. "

Вызов с экземпляром объекта

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

Вызов из метода объекта

Вызов метода [) разрешается с использованием типа времени выполнения, но статические вызовы метода class разрешаются с использованием типа времени компиляции (объявленного).

Изменение правил

Чтобы изменить эти правила, последний вызов в примере с именем Child.printValue(), статические вызовы должны были быть снабжены типом во время выполнения, а не компилятором, разрешающим вызов во время компиляции с объявленным классом объекта (или контекста). Статические вызовы затем могут использовать иерархию (динамического) типа для разрешения вызова, так же, как и вызовы метода объекта.

Это легко выполнимо (если мы изменили Java: -O) и не на все это необоснованно, однако у него есть некоторые интересные соображения.

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

At момент, Java имеет этот «quirk» на языке, на котором вызовы obj.staticMethod() заменяются вызовами ObjectClass.staticMethod() (обычно с предупреждением). [ Примечание: ObjectClass является типом времени компиляции obj.] Это были бы хорошие кандидаты для переопределения таким образом, принимая тип времени выполнения obj.

Если бы мы это сделали, это затрудняло бы считывание методов: статические вызовы в родительском классе потенциально могли бы быть динамически «перенаправлены». Чтобы избежать этого, нам пришлось бы вызвать статический метод с именем класса - и это делает вызовы более понятными с иерархией типов времени компиляции (как сейчас).

Другие способы вызова статический метод более сложный: this.staticMethod() должен означать то же, что и obj.staticMethod(), принимая тип времени выполнения this. Однако это может привести к некоторым головным болям с существующими программами, которые вызывают (видимо, локальные) статические методы без декорации (что, возможно, эквивалентно this.method()).

Так что насчет неуправляемых вызовов staticMethod()? Я предлагаю им сделать то же самое, что и сегодня, и использовать контекст локального класса, чтобы решить, что делать. В противном случае возникла бы большая путаница. Конечно, это означает, что method() будет означать this.method(), если method был нестационарным методом, а ThisClass.method(), если method был статическим методом. Это еще один источник путаницы.

Другие соображения

Если мы изменили это поведение (и сделало статические вызовы потенциально динамически нелокальными), мы, вероятно, захотели бы пересмотреть смысл final, private и protected в качестве квалификаторов в static методах класса. Мы тогда должны были привыкнуть к тому, что методы private static и public final не переопределены и поэтому могут быть безопасно разрешены во время компиляции и «безопасны» для чтения в качестве локальных ссылок.

37
ответ дан Steve Powell 18 August 2018 в 21:24
поделиться
  • 1
    «Если бы мы это сделали, это затрудняло бы считывание методов: статические вызовы в родительском классе потенциально могли бы быть динамически« перенаправлены ». & quot; Правда, но это именно то, что происходит с обычными вызовами нестатических функций. Это обычно рекламируется как положительная особенность обычных виртуальных функций, а не проблема. – Jay 17 April 2015 в 10:32

Переопределение на Java просто означает, что конкретный метод будет вызываться на основе типа времени выполнения объекта, а не его типа времени компиляции (что имеет место с переопределенными статическими методами). Поскольку статические методы являются методами класса, они не являются методами экземпляра, поэтому они не имеют никакого отношения к тому, какая ссылка указывает на какой объект или экземпляр, потому что из-за характера статического метода он относится к определенному классу. Вы можете обновить его в подклассе, но этот подкласс ничего не знает о статических методах родительского класса, поскольку, как я уже сказал, он специфичен только для того класса, в котором он был объявлен. Доступ к ним с использованием ссылок на объекты - это дополнительная свобода, предоставляемая разработчиками Java, и мы, конечно же, не должны останавливаться на этой практике, только когда они ограничивают ее более подробными сведениями и примером http://faisalbhagat.blogspot.com/2014/ 09 / способ-наиважнейшая-и-метод-hiding.html

2
ответ дан xuesheng 18 August 2018 в 21:24
поделиться
7
ответ дан KIN 7 September 2018 в 05:16
поделиться
7
ответ дан KIN 30 October 2018 в 09:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: