Чому не можна перекрити статичні методи?
Якщо можливо, скористайтеся прикладом.
Parent p = new Child()
і тоді p.childOverriddenStaticMethod()
компілятор вирішить це Parent.childOverriddenStaticMethod()
, переглянувши тип посилання.
Чому не можна перекрити статичні методи?
Якщо можливо, скористайтеся прикладом.
Parent p = new Child()
і тоді p.childOverriddenStaticMethod()
компілятор вирішить це Parent.childOverriddenStaticMethod()
, переглянувши тип посилання.
Відповіді:
Переопределення залежить від наявності примірника класу. Суть поліморфізму полягає в тому, що ви можете підкласифікувати клас, і об’єкти, що реалізують ці підкласи, матимуть різну поведінку для тих же методів, що визначені в надкласі (і переопределено в підкласах). Статичний метод не асоціюється з жодним екземпляром класу, тому поняття не застосовується.
Дизайн Java, який вплинув на це, вплинув на два аспекти. Одне з них викликало занепокоєння щодо продуктивності: у Smalltalk було багато критики щодо того, що він занадто повільний (збирання сміття та поліморфні дзвінки є частиною цього), і творці Java вирішили цього уникнути. Іншим було рішення, що цільовою аудиторією для Java були розробники C ++. Здійснення роботи статичних методів так, як вони, принесло користь знайомству програмістів на C ++, а також було дуже швидким, оскільки не потрібно чекати часу виконання, щоб з'ясувати, до якого методу зателефонувати.
objects
) дозволяють перевантажувати методи.
obj.staticMethod()
), який дозволений та використовує типи часу компіляції. Коли статичний виклик знаходиться в нестатичному методі класу, "поточний" об'єкт може бути похідним типом класу, але статичні методи, визначені для похідних типів, не враховуються (вони знаходяться у типі виконання ієрархія).
Особисто я вважаю, що це недолік в дизайні 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");
}
}
Цей код не працюватиме, як ви могли очікувати. А саме, SpecialE zaposee отримує бонус у розмірі 2%, як і постійні працівники. Але якщо ви видалите "статичні", то SpecialE zaposee отримає 3% бонус.
(Правда, цей приклад - поганий стиль кодування, оскільки в реальному житті ви, швидше за все, хочете, щоб мультиплікатор бонусів знаходився десь у базі даних, а не в жорсткому коді. Але це лише тому, що я не хотів багато збивати приклад коду, не має значення для пункту.)
Мені здається цілком правдоподібним, що ви, можливо, захочете зробити getBonusMultiplier статичним. Можливо, ви хочете мати можливість відображати мультиплікатор бонусу для всіх категорій працівників, не потребуючи примірника працівника в кожній категорії. Який би був сенс пошуку таких прикладів? Що робити, якщо ми створюємо нову категорію працівника і не маємо до неї жодних працівників? Це цілком логічно статична функція.
Але це не працює.
І так, так, я можу придумати будь-яку кількість способів переписати вищевказаний код, щоб він працював. Моя думка не в тому, що це створює нерозв’язну проблему, а в тому, що створює пастку для необережного програміста, оскільки мова веде себе не так, як я думаю, що розумна людина очікувала б.
Можливо, якби я спробував написати компілятор для мови OOP, я швидко зрозумів би, чому реалізувати його, щоб статичні функції можна було перекрити було б складно чи неможливо.
Або, можливо, є якась вагома причина, чому Java поводиться так. Чи може хтось вказати на перевагу такої поведінки, якусь категорію проблем, яку це полегшує? Я маю на увазі, не просто вказуйте мені на специфікацію мови Java і скажіть "бачте, це документально підтверджено, як вона поводиться". Я знаю це. Але чи є вагома причина, чому вона ДОЛЖЕН би поводитись так? (Окрім очевидного "змусити його працювати правильно було занадто важко" ...)
Оновлення
@VicKirk: Якщо ви маєте на увазі, що це "поганий дизайн", оскільки він не відповідає тому, як Java обробляє статику, моя відповідь: "Ну, так, звичайно". Як я вже говорив у своєму початковому дописі, він не працює. Але якщо ви маєте на увазі, що це поганий дизайн в тому сенсі, що було б щось принципово неправильно з мовою, де це працює, тобто там, де статику можна було б замінити так само, як і віртуальні функції, то це якось внесе двозначність, або це буде неможливо Ефективно впроваджуючи щось подібне, я відповідаю: "Чому? Що не так з концепцією?"
Я думаю, що приклад, який я даю, - це дуже природна річ, яку хочу зробити. У мене є клас, який має функцію, яка не залежить від будь-яких даних про екземпляр, і яку, можливо, я б дуже хотів викликати незалежно від екземпляра, а також хотів зателефонувати з методу екземпляра. Чому це не повинно працювати? Я зіткнувся з цією ситуацією досить багато разів за ці роки. На практиці я її обходжу, роблячи функцію віртуальною, а потім створюю статичний метод, єдиною метою якого в житті є статичний метод, який передає виклик віртуальному методу з манекеном. Це здається дуже крутим способом дістатися туди.
someStatic()
а B поширюється на A, то B.someMethod()
прив'язується до методу в А. Якщо я згодом додаю someStatic()
до B, викликовий код все ще викликає, A.someStatic()
поки я не перекомпілюю код виклику. Також мене здивувало, що bInstance.someStatic()
використовує оголошений тип bInstance, а не тип виконання, оскільки він прив'язується при компіляції не посиланням, тому A bInstance; ... bInstance.someStatic()
викликає A.someStatic (), якщо існує B.someStatic ().
Коротка відповідь: це цілком можливо, але 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.
Тут єдиний випадок, який може стати несподіванкою (і стосовно якого йдеться про це), є першим випадком:
"Тип запуску не використовується для визначення того, які статичні методи викликаються, навіть коли вони викликаються об'єктом об'єкта ( obj.staticMethod()
)."
і останній випадок:
"При виклику статичного методу всередині об'єктного методу класу обраний статичний метод - це той, який доступний із самого класу, а не з класу, що визначає тип об'єкта часу виконання."
Статичний виклик вирішується під час компіляції, тоді як нестатичний виклик методу вирішується під час виконання. Зауважте, що хоча статичні методи успадковуються (від батьків), вони не перекриваються (від дитини). Це може стати сюрпризом, якщо ви очікували іншого.
Виклики методу об'єкта вирішуються за допомогою типу запуску, але статичні ( класові ) виклики методу вирішуються за допомогою типу компіляції (оголошеного).
Щоб змінити ці правила, щоб останній виклик у наведеному прикладі Child.printValue()
статичним викликам повинен був надавати тип під час виконання, а не компілятор, який розв'язує виклик під час компіляції із заявленим класом об'єкта (або контекст). Тоді статичні виклики можуть використовувати (динамічний) тип ієрархії для вирішення виклику, як і сьогодні.
Це легко зробити (якщо ми змінили Java: -O), і це зовсім нерозумно, проте, це має деякі цікаві міркування.
Основна думка полягає в тому, що нам потрібно вирішити, які статичні виклики методу повинні це робити.
На даний момент у Java є ця "химерність" у мові, згідно з якою 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
методи не перевизначений, і тому може бути безпечно вирішене під час компіляції, і є «безпечними» , щоб читати місцеві посилання.
Насправді ми помилялися.
Незважаючи на те, що Java не дозволяє переосмислити статичні методи за замовчуванням, якщо детально ознайомитись з документацією класів Class і Method на 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 як RegularEслужбовця та призначимо їй екземпляр SpecialE Employee, ми все одно матимемо виклик методу RegularE Employee у першому випадку та виклик методу SpecialE Employee у другому випадку
RegularEmployee Carl = new SpecialEmployee();
System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());
просто подивіться на вихідну консоль:
0.02
0.03
;)
Статичні методи 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. Я не знаю, чому вони коли-небудь дозволяли цей ярлик.
clazz2 instanceof clazz1
правильного використання ви можете замість цього використовувати class2.isAssignableFrom(clazz1)
, що, на мою думку, повернеться правдою у вашому прикладі.
Переосмислення методу стає можливим завдяки динамічній диспетчеризації , тобто, оголошений тип об'єкта не визначає його поведінку, а, скоріше, його тип виконання:
Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"
Незважаючи на те, що обидва lassie
і kermit
оголошені як об'єкти типу Animal
, їх поведінка (метод .speak()
) змінюється, оскільки динамічна диспетчеризація прив'язуватиме виклик методу .speak()
до реалізації лише під час виконання, а не під час компіляції.
Тепер ось, де static
ключове слово починає мати сенс: слово "статичний" є антонімом "динамічний". Тож причина, чому ви не можете перекрити статичні методи, полягає в тому, що не існує динамічної диспетчеризації статичних членів - тому що статичний буквально означає "не динамічний". Якщо вони динамічно розсилаються (і, таким чином, їх можна було б замінити), static
ключове слово просто не матиме сенсу більше.
Так. Практично Java дозволяє переважати статичний метод, і немає теоретично, якщо ви перекриєте статичний метод на Java, він буде компілюватися і запускатися плавно, але він втратить поліморфізм, який є основною властивістю Java. Ви читатимете всюди, що неможливо спробувати самостійно компілювати та працювати. ви отримаєте свою відповідь. Наприклад, якщо у вас є тварини класу і статичний метод eat (), і ви перекриєте цей статичний метод у своєму підкласі, він називає його Dog. Тоді, коли б ви не присвоїли об'єкту Собаку Довідку про тварин і зателефонували eat () відповідно до Java Dog’s eat (), слід було б зателефонувати, але в статичному Переважному з'їденні тварини () буде називатися.
class Animal {
public static void eat() {
System.out.println("Animal Eating");
}
}
class Dog extends Animal{
public static void eat() {
System.out.println("Dog Eating");
}
}
class Test {
public static void main(String args[]) {
Animal obj= new Dog();//Dog object in animal
obj.eat(); //should call dog's eat but it didn't
}
}
Output Animal Eating
Відповідно до принципу поліморфізму Java, вихід повинен бути Dog Eating
.
Але результат був іншим, оскільки для підтримки поліморфізму Java використовує пізнє прив'язування, що означає, що методи викликаються лише під час виконання, але не у випадку статичних методів. У статичних методах компілятор викликає методи на час компіляції, а не на час виконання, тому ми отримуємо методи відповідно до посилання, а не відповідно до об'єкта, посилання, що містить, тому ви можете сказати, що він практично підтримує статичне перекриття, але теоретично це не робить 'т.
переосмислення зарезервовано для членів, наприклад, для підтримки поліморфної поведінки. Члени статичного класу не належать до певного екземпляра. натомість статичні члени належать до класу, і в результаті перевизначення не підтримується, оскільки підкласи успадковують лише захищені та публічні члени екземплярів, а не статичні члени. Ви можете визначити шаблони дизайну фабрики та / або стратегії дослідження, щоб оцінити альтернативний підхід.
У Java (і багатьох мовах OOP, але я не можу говорити назавжди; а деякі взагалі не мають статики) усі методи мають фіксовану підпис - параметри та типи. У віртуальному методі мається на увазі перший параметр: посилання на сам об'єкт, і коли його викликає всередині об'єкта, компілятор автоматично додаєthis
.
Для статичних методів різниці немає - вони все ще мають фіксований підпис. Однак, оголосивши метод статичним, ви чітко заявили, що компілятор не повинен включати параметр об'єкта, що мається на увазі, на початку цієї підпису. Тому будь-який інший код, який викликає це, не повинен намагатися ставити посилання на об'єкт у стеку . Якби це зробило це, то виконання методу не працюватиме, оскільки параметри опиняться в неправильному місці - зміщеному на один - на стеці.
Через цю різницю між двома; віртуальні методи завжди мають посилання на контекстний об'єкт (тобто this
), тож можна посилатися на все, що знаходиться в купі, що належить до цього примірника об'єкта. Але при статичних методах, оскільки немає посилання, цей метод не може отримати доступ до будь-яких змінних об'єктів і методів, оскільки контекст не відомий.
Якщо ви хочете, щоб Java змінила визначення, щоб контекст об'єкта передався для кожного методу, статичного або віртуального, ви, по суті, мали б лише віртуальні методи.
Як хтось запитав у коментарі до оп - яка ваша причина та мета, щоб бажати цієї функції?
Я не знаю Рубі багато, оскільки про це згадував ОП, я робив деякі дослідження. Я бачу, що в класах Ruby - це дійсно особливий вид об’єкта і можна створювати (навіть динамічно) нові методи. Класи - це об'єкти повного класу в Ruby, їх немає на Java. Це лише те, що вам доведеться прийняти під час роботи з Java (або C #). Це не динамічні мови, хоча C # додає деякі форми динаміки. Насправді у Рубі немає «статичних» методів, наскільки я міг знайти - у такому випадку це методи на об'єкті класу синглів. Потім ви можете замінити цей синглтон з новим класом, і методи в об'єкті попереднього класу будуть викликати ті, які визначені в новому класі (правильно?). Отже, якщо ви назвали метод у контексті оригінального класу, він все одно виконає лише оригінальну статику, але виклик методу у похідному класі буде викликати методи або з батьківського або підкласу. Цікаво, і я бачу в цьому певну цінність. Він приймає іншу думку.
Оскільки ви працюєте на Java, вам потрібно буде налаштуватись на такий спосіб виконання дій. Чому вони це зробили? Ну, мабуть, щоб покращити продуктивність на той час, виходячи з наявних технологій та розуміння. Комп'ютерні мови постійно розвиваються. Поверніться досить далеко, і немає такого поняття, як OOP. В майбутньому з’являться інші нові ідеї.
EDIT : Ще один коментар. Тепер, коли я бачу відмінності і як сам Java / C # розробник, я можу зрозуміти, чому відповіді, які ви отримуєте від розробників Java, можуть збивати з пантелику, якщо ви йдете з такої мови, як Ruby. static
Методи Java не такі, як class
методи Ruby . Java-розробникам буде важко це зрозуміти, як, навпаки, тим, хто працює переважно з такою мовою, як Ruby / Smalltalk. Я бачу, як це також сильно заплутає той факт, що Java також використовує "метод класу" як інший спосіб говорити про статичні методи, але цей самий термін Рубі використовує по-різному. У Java немає методів класичного стилю Ruby (вибачте); У Ruby немає статичних методів стилю Java, які насправді є лише старими функціями процедурного стилю, як це знайдено у C.
До речі - дякую за запитання! Сьогодні я дізнався щось нове для мене про методи класу (стиль Ruby).
Ну ... відповідь "НІ", якщо ви думаєте з точки зору того, як має переважати метод, який має переважати на 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 ()" на об'єкті типу виконання як "Підклас", а не як "Суперклас". Це підтверджує, що статичні методи завжди вирішуються, використовуючи лише інформацію про час компіляції.
Взагалі не має сенсу допускати "переосмислення" статичних методів, оскільки не було б хорошого способу визначити, який з них викликати під час виконання. Беручи до прикладу Співробітник, якщо ми називаємо RegularE Employee.getBonusMultiplier () - який метод слід виконати?
У випадку з Java, можна уявити визначення мови, де можна "переосмислити" статичні методи, доки вони викликаються через об'єкт об'єкта. Однак все це робиться - це повторна реалізація методів звичайного класу, додаючи надлишок мови, не додаючи жодної користі.
Переосмислюючи, ми можемо створити поліморфну природу залежно від типу об'єкта. Статичний метод не має відношення до об'єкта. Тож java не може підтримувати переосмислення статичного методу.
Мені подобається і подвійний коментар Джея ( https://stackoverflow.com/a/2223803/1517187 ).
Я згоден, що це поганий дизайн Java.
Багато інших мов підтримують переважаючі статичні методи, як ми бачимо в попередніх коментарях. Я відчуваю, що Джей також прийшов на Яву з Delphi, як я.
Delphi (Object Pascal) була першою мовою, що реалізувала OOP.
Очевидно, що багато людей мали досвід роботи з цією мовою, оскільки в минулому вона була єдиною мовою для написання комерційних GUI-продуктів. І - так, ми могли б у Delphi перекрити статичні методи. Насправді статичні методи в Delphi називають "класовими методами", тоді як у Delphi була інша концепція "статичних методів Delphi", які були методами з ранньою зв'язуванням. Щоб замінити методи, вам довелося скористатися пізнім прив'язкою, оголосити "віртуальну" директиву. Так що це було дуже зручно та інтуїтивно, і я би очікував цього на Java.
Яка користь принесе перевагу статичним методам. Статичні методи не можна викликати через екземпляр.
MyClass.static1()
MySubClass.static1() // If you overrode, you have to call it through MySubClass anyway.
EDIT: Схоже, що через невдалий нагляд за мовним дизайном, ви можете викликати статичні методи через екземпляр. Взагалі ніхто цього не робить. Моє ліжко.
variable.staticMethod()
замість того Class.staticMethod()
, де variable
є змінна із заявленим типом Class
. Я згоден, це поганий дизайн мови.
Перевизначення в Java просто означає, що конкретний метод буде називатися на основі типу виконання об'єкта, а не на його типі часу компіляції (що має місце при перекритих статичних методах). Оскільки статичні методи - це методи класу, вони не є методами екземплярів, тому вони не мають нічого спільного з тим фактом, який посилання вказує на який об’єкт чи екземпляр, оскільки через характер статичного методу він належить до певного класу. Ви можете передекларувати його в підкласі, але цей підклас не буде знати нічого про статичні методи батьківського класу, оскільки, як я вже сказав, він характерний лише для того класу, в якому він оголошений. Доступ до них за допомогою посилань на об’єкти - це лише додаткова свобода, яку надають дизайнери Java, і ми, звичайно, не повинні думати про припинення цієї практики лише тоді, коли вони обмежують її детальніше та прикладом http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html
Переосмислюючи, ви досягаєте динамічного поліморфізму. Коли ви говорите переважаючі статичні методи, слова, які ви намагаєтесь використовувати, суперечать один одному.
Статичний говорить - час компіляції, переосмислення використовується для динамічного поліморфізму. Обидва є протилежними за своєю природою, і тому їх не можна використовувати разом.
Динамічна поліморфна поведінка виникає, коли програміст використовує об'єкт і звертається до методу екземпляра. JRE буде відображати різні методи екземплярів різних класів на основі того, який тип об'єкта ви використовуєте.
Коли ви говорите про переосмислення статичних методів, до статичних методів ми отримаємо доступ, використовуючи ім’я класу, яке буде пов’язане під час компіляції, тому не існує концепції зв’язування методів під час виконання зі статичними методами. Отже, сам термін "переосмислення" статичних методів не має жодного значення.
Примітка: навіть якщо ви звертаєтесь до методу класу з об'єктом, компілятор Java все-таки досить розумний, щоб знайти його, і він буде робити статичне посилання.
Box.createBox
має більше сенсу, ніж BoxFactory.createBox
, і є неминучим зразком, коли потрібно перевірити конструкцію помилок, не кидаючи винятки (конструктори не можуть вийти з ладу, вони можуть лише вбити процес / кинути виняток), тоді як статичний метод може повернути нуль при відмові, або навіть прийняти зворотний виклик успіху / помилки, щоб написати щось на зразок hastebin.com/codajahati.java .
Відповідь на це запитання проста: метод або змінна, позначена як статична, належить лише класу, тому статичний метод не може бути успадкований у підкласі, оскільки вони належать лише до суперкласу.
Просте рішення: Використовуйте одиночний екземпляр. Це дозволить скасувати і успадкувати.
У своїй системі у мене є клас SingletonsRegistry, який повертає екземпляр для пройденого класу. Якщо екземпляр не знайдений, він створюється.
Мовний клас Хакс:
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!";
}
}
Singleton.get()
. Реєстр є просто накладними, і він виключає GC для класів.
Статичний метод, змінний, блок або вкладений клас належить до всього класу, а не до об'єкта.
Метод на Java використовується для викриття поведінки об'єкта / класу. Тут, оскільки метод є статичним (тобто статичний метод використовується для представлення поведінки класу.) Зміна / переосмислення поведінки всього класу буде порушувати явище одного з основних стовпчиків об'єктно-орієнтованого програмування, тобто високої згуртованості . (пам'ятайте, що конструктор - це особливий вид методу на Java.)
Висока згуртованість - один клас повинен мати лише одну роль. Наприклад: Автомобільний клас повинен виробляти лише автомобільні об'єкти, а не велосипед, вантажівки, літаки тощо. Але клас «Автомобіль» може мати деякі особливості (поведінку), які належать лише йому самим.
Тому, розробляючи мову програмування 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 () оголошено як остаточний, код не буде компілюватися, оскільки кінцеве ключове слово обмежує програміст повторно оголошувати метод.
public static final int getVehileNumber() {
return VIN; }
Взагалі, це розробники програмного забезпечення, які використовуються, де використовувати статичні методи. Я особисто вважаю за краще використовувати статичні методи для виконання деяких дій, не створюючи жодного примірника класу. По-друге, приховати поведінку класу від зовнішнього світу.
Ось просте пояснення. Статичний метод асоціюється з класом, тоді як метод екземпляра асоціюється з певним об'єктом. Заміни дозволяють викликати різну реалізацію методів, що переосмислюються, пов'язаних з конкретним об'єктом. Тож перекрити статичний метод, котрий навіть не пов'язаний з об'єктами, а сам клас в першу чергу, є протиінтуїтивним. Таким чином, статичні методи не можуть бути відмінені на основі того, що об'єкт викликає його, він завжди буде пов'язаний з класом, де він був створений.
public abstract IBox createBox();
зрозуміти наявність внутрішнього інтерфейсу IBox? Box може реалізувати IBox, щоб перекрити createBox, і створити результат об'єкта в дійсному IBox, інакше повернути нуль. Конструктори не можуть повернути "null", і тому ви змушені (1) використовувати винятки КОЖНЕ (що ми робимо зараз), або (2) створювати фабричні класи, які роблять те, про що я говорив раніше, але таким чином, що не має сенсу ні для початківців, ні для експертів Java (що ми також робимо зараз). Статичні нереалізовані методи вирішують це.
Тепер, бачачи відповіді вище, всі знають, що ми не можемо перекрити статичні методи, але не слід неправильно розуміти концепцію доступу до статичних методів з підкласу .
Ми можемо отримати доступ до статичних методів суперкласу з посиланням на підклас, якщо цей статичний метод не був прихований новим статичним методом, визначеним у підкласі.
Наприклад, див. Нижче код: -
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 та шукай, що можна зробити в підкласі .
Дякую
Наступний код показує, що це можливо:
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();
}
}
osm
є OverridenStaticMeth
НЕ OverrideStaticMeth
.