Statik yöntemleri geçersiz kılmak neden mümkün değildir?
Mümkünse, lütfen bir örnek kullanın.
Parent p = new Child()
ve p.childOverriddenStaticMethod()
derleyici Parent.childOverriddenStaticMethod()
referans türüne bakarak çözecektir .
Statik yöntemleri geçersiz kılmak neden mümkün değildir?
Mümkünse, lütfen bir örnek kullanın.
Parent p = new Child()
ve p.childOverriddenStaticMethod()
derleyici Parent.childOverriddenStaticMethod()
referans türüne bakarak çözecektir .
Yanıtlar:
Geçersiz kılma, bir sınıf örneğine sahip olmasına bağlıdır. Polimorfizmin amacı, bir sınıfı alt sınıflandırabilmenizdir ve bu alt sınıfları uygulayan nesneler, üst sınıfta tanımlanan (ve alt sınıflarda geçersiz kılınan) aynı yöntemler için farklı davranışlara sahip olacaktır. Statik bir yöntem, sınıfın herhangi bir örneğiyle ilişkilendirilmez, bu nedenle kavram uygulanabilir değildir.
Java'nın tasarımını etkileyen, bunu etkileyen iki husus vardı. Birisi performansla ilgili bir kaygıydı: Smalltalk'ın çok yavaş olduğuna dair çok fazla eleştiri vardı (çöp toplama ve polimorfik çağrılar bunun bir parçasıydı) ve Java'nın yaratıcıları bundan kaçınmaya kararlıydı. Bir diğeri, Java için hedef kitlenin C ++ geliştiricileri olduğu kararıydı. Statik yöntemlerin C ++ programcıları için aşina olma avantajına sahip oldukları şekilde çalışmasını sağlamak ve aynı zamanda çok hızlıydı, çünkü hangi yöntemi çağıracağınızı anlamak için çalışma zamanına kadar beklemeye gerek yoktur.
objects
) eşdeğeri yöntemlerin aşırı yüklenmesine izin verir.
obj.staticMethod()
) izin verilen ve derleme zamanı türlerini kullanan statik bir yöntem çağrıldığında olur . Statik çağrı bir sınıfın statik olmayan bir yöntemindeyse, "current" nesnesi sınıfın türetilmiş bir türü olabilir - ancak türetilen türlerde tanımlanan statik yöntemler dikkate alınmaz (çalışma zamanı türündedir hiyerarşi).
Şahsen bu Java tasarımında bir kusur olduğunu düşünüyorum. Evet, evet, statik yöntemler bir sınıfa vb. Bağlıyken statik olmayan yöntemlerin bir örneğe eklendiğini anlıyorum. Yine de aşağıdaki kodu göz önünde bulundurun:
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");
}
}
Bu kod beklediğiniz gibi çalışmaz. Yani, SpecialEmployee normal çalışanlar gibi% 2 bonus kazanır. Ancak "statik" leri kaldırırsanız, SpecialEmployee% 3 bonus kazanır.
(Kuşkusuz, bu örnek, gerçek hayatta bonus çarpanının sabit kodlu değil bir yerde bir veritabanında olmasını isteyeceğiniz için kötü kodlama tarzıdır. noktayla alakasız kod.)
GetBonusMultiplier'ı statik hale getirmek isteyebileceğiniz benim için oldukça makul görünüyor. Belki de, her kategoride bir çalışanın bir örneğine ihtiyaç duymadan, tüm çalışan kategorileri için bonus çarpanını görüntülemek isteyebilirsiniz. Bu örnek örnekleri aramanın anlamı ne olabilir? Yeni bir çalışan kategorisi oluşturuyor ve buna henüz bir çalışan atanmamışsa ne olur? Bu oldukça mantıklı bir statik işlevdir.
Ama işe yaramıyor.
Ve evet, evet, çalışması için yukarıdaki kodu yeniden yazmak için herhangi bir sayıda yol düşünebilirsiniz. Demek istediğim, çözülemez bir sorun yarattığı değil, aynı zamanda dikkatsiz bir programcı için bir tuzak yarattığıdır, çünkü mantıklı bir insanın beklediği gibi dil davranmaz.
Belki bir OOP dili için bir derleyici yazmaya çalışırsam, statik fonksiyonların geçersiz kılınabilmesi için neden uygulandığını hızlı bir şekilde anlayabilirdim.
Ya da Java'nın bu şekilde davranmasının iyi bir nedeni olabilir. Herkes bu davranış bir avantaj, bu daha kolay hale getirilen bir sorun kategorisine işaret edebilir? Demek istediğim, beni sadece Java dil spesifikasyonuna yönlendirmeyin ve "bakın, bu nasıl davrandığını belgeledi" deyin. Bunu biliyorum. Ancak bu şekilde davranması için iyi bir neden var mı? (Açıkça "doğru çalışmasını sağlamak çok zordu" ...)
Güncelleme
@VicKirk: Bunun "kötü tasarım" olduğunu söylüyorsanız, Java'nın statik işlemleri nasıl gerçekleştirdiğine uymuyorsa, cevabım "Tabii ki, tabii ki". Orijinal yazımda söylediğim gibi, işe yaramıyor. Ancak, bunun çalıştığı bir dille, yani statikin sanal işlevler gibi geçersiz kılınabileceği bir dille temelde yanlış bir şey olacağı anlamında kötü bir tasarım olduğunu kastediyorsanız, bunun bir şekilde bir belirsizlik getireceği veya verimli bir şekilde ya da böyle bir şekilde, "Neden? Kavramın nesi yanlış?"
Sanırım verdiğim örnek yapmak çok doğal bir şey. Herhangi bir örnek verilere bağlı olmayan ve çok makul bir örnek bağımsız çağırmak yanı sıra bir örnek yöntemi içinde aramak isteyen bir işlevi olan bir sınıf var. Bu neden işe yaramıyor? Bu duruma yıllar içinde oldukça fazla rastladım. Uygulamada, işlevi sanal hale getirerek ve sonra hayattaki tek amacı çağrıyı kukla bir örnekle sanal yönteme geçiren statik bir yöntem olmak olan statik bir yöntem oluşturarak bu sorunu çözüyorum. Buraya ulaşmak için çok dolambaçlı bir yol gibi görünüyor.
someStatic()
ve B A'yı genişletirse, A'daki yönteme B.someMethod()
bağlanırsa . Daha sonra someStatic()
B'ye A.someStatic()
eklersem, arama kodunu yeniden derleyene kadar arama kodu hala çağırır . Ayrıca derleme değil bağlanır, çünkü çalışma zamanı türü değil bInstance.someStatic()
, bildirilen bInstance türü kullanan beni şaşırttı , bu nedenle A bInstance; ... bInstance.someStatic()
eğer B.someStatic () varsa A.someStatic () çağırır.
Kısa cevap: tamamen mümkündür, ancak Java bunu yapmaz.
Java'daki mevcut durumu gösteren bazı kodlar :
Dosya 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();
}
}
Dosya 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();
}
}
Bunu çalıştırırsanız (Mac 1.6'da, Eclipse'den Java 1.6 kullanarak yaptım) şunları elde edersiniz:
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.
Burada, sürpriz olabilecek (ve sorunun hakkında olduğu) sadece vakalar ilk durum gibi görünüyor:
"Çalışma zamanı türü, bir nesne örneği ( obj.staticMethod()
) ile çağrıldığında bile hangi statik yöntemlerin çağrıldığını belirlemek için kullanılmaz ."
ve son dava:
"Bir sınıfın nesne yönteminin içinden statik bir yöntem çağırırken, seçilen statik yöntem , nesnenin çalışma zamanı türünü tanımlayan sınıftan değil , sınıfın kendisinden erişilebilir yöntemdir ."
Statik çağrı derleme zamanında çözümlenirken, statik olmayan yöntem çağrısı çalışma zamanında çözümlenir. Statik yöntemler miras alınsa da (ebeveynden) geçersiz kılınmadıklarına (çocuk tarafından) dikkat edin. Aksi takdirde bu bir sürpriz olabilir.
Nesne yöntemi çağrıları çalışma zamanı türü kullanılarak çözümlenir, ancak statik ( sınıf ) yöntem çağrıları derleme zamanı (bildirilen) türü kullanılarak çözümlenir.
Bu kuralları değiştirmek için, çağrılan örnekteki son Child.printValue()
çağrının, derleme zamanında çağrıyı nesnenin bildirilen sınıfıyla çözümlemesi yerine derleyici yerine statik çağrılara bir tür sağlanmalıdır. bağlamı). Statik çağrılar, tıpkı nesne yöntemi çağrılarının bugün yaptığı gibi çağrıyı çözmek için (dinamik) tür hiyerarşisini kullanabilir.
Bu kolayca yapılabilir (Java: -O değiştirirsek) ve hiç de mantıksız değildir, ancak bazı ilginç hususlar vardır.
Ana düşünce, hangi statik yöntem çağrılarının bunu yapması gerektiğine karar vermemiz gerektiğidir .
Şu anda Java, obj.staticMethod()
çağrıların ObjectClass.staticMethod()
çağrılarla (normalde bir uyarı ile) değiştirildiği dilde "tuhaflığa" sahiptir . [ Not: ObjectClass
derleme zamanı türüdür obj
.] Bunlar, çalışma zamanı türünü alarak bu şekilde geçersiz kılmak için iyi adaylar olacaktır obj
.
Eğer yapsaydık, yöntem gövdelerinin okunmasını zorlaştırırdı: bir üst sınıftaki statik çağrılar, dinamik olarak "yeniden yönlendirilebilir". Bundan kaçınmak için, statik adı bir sınıf adıyla çağırmamız gerekir ve bu da çağrıları derleme zamanı tür hiyerarşisiyle daha açık bir şekilde çözülür (şimdi olduğu gibi).
Statik bir yöntemi çağırmanın diğer yolları daha zordur: çalışma zamanı türünü almakla this.staticMethod()
aynı anlama gelmelidir . Bununla birlikte, bu durum, mevcut programlarla süssüz (görünüşte yerel) statik yöntemleri çağıran (muhtemelen eşdeğerdir ) bazı baş ağrılarına neden olabilir .obj.staticMethod()
this
this.method()
Peki ya süssüz aramalar staticMethod()
? Bugünle aynı şeyi yapmalarını ve ne yapılacağına karar vermek için yerel sınıf bağlamını kullanmalarını öneriyorum. Aksi takdirde büyük bir karışıklık ortaya çıkar. Tabii ki demektir method()
anlamına gelecektir this.method()
eğer method
bir statik olmayan yöntem olduğunu ve ThisClass.method()
eğer method
statik bir metod idi. Bu başka bir karışıklık kaynağı.
Biz bu davranışı değişti (ve statik aramaları potansiyel dinamik yerel olmayan yapılmış), biz muhtemelen anlamını tekrar istediğiniz final
, private
ve protected
eleme olarak static
bir sınıf yöntemleri. O zaman hepimiz private static
ve public final
yöntemlerin geçersiz kılınmadığına ve bu nedenle derleme zamanında güvenli bir şekilde çözülebileceğine ve yerel referanslar olarak okumak için "güvenli" olduğumuza alışmalıyız.
Aslında yanılmışız.
Java, varsayılan olarak statik yöntemleri geçersiz kılmanıza izin vermese de, Java'daki Sınıf ve Yöntem sınıflarının belgelerine tam olarak bakarsanız, aşağıdaki geçici çözümü uygulayarak statik yöntemleri geçersiz kılmanın bir yolunu bulabilirsiniz:
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());
}
}
Ortaya çıkan çıktı:
0.02
0.02
0.02
0.03
elde etmeye çalıştıklarımız :)
Üçüncü değişkeni Carl'ı RegularEmployee olarak tanımlasak ve buna SpecialEmployee örneği atadığımızda bile, ilk durumda RegularEmployee yöntemini ve ikinci durumda SpecialEmployee yöntemini çağrıyoruz.
RegularEmployee Carl = new SpecialEmployee();
System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());
sadece çıkış konsoluna bakın:
0.02
0.03
;)
Statik yöntemler JVM tarafından genel olarak ele alınır, hiçbir nesne örneğine bağlı değildir.
Sınıf nesnelerinden (Smalltalk gibi dillerde olduğu gibi) statik yöntemleri çağırabiliyorsanız kavramsal olarak mümkün olabilir, ancak Java'da durum böyle değildir.
DÜZENLE
Statik yöntemi aşırı yükleyebilirsiniz , sorun değil. Ancak , sınıf birinci sınıf bir nesne olmadığından statik bir yöntemi geçersiz kılamazsınız. Yansıma, bir nesnenin sınıfını çalışma zamanında almak için kullanabilirsiniz, ancak aldığınız nesne sınıf hiyerarşisine paralel değildir.
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
Sınıfları yansıtabilirsiniz, ama orada durur. Kullanarak değil clazz1.staticMethod()
, kullanarak statik bir yöntem çağırırsınız MyClass.staticMethod()
. Statik bir yöntem bir nesneye bağlı değildir ve dolayısıyla statik bir yöntemde this
ne de ne super
bir kavram vardır . Statik bir yöntem global bir işlevdir; sonuç olarak, polimorfizm nosyonu da yoktur ve bu nedenle, yöntem geçersiz kılma anlamsızdır.
Ancak bu MyClass
, Smalltalk'taki gibi bir yöntemi çağırdığınız çalışma zamanında bir nesne olsaydı mümkün olabilir (veya belki bir yorum olarak JRuby, ama JRuby hakkında hiçbir şey bilmiyorum).
Evet, bir şey daha. Bir nesne aracılığıyla statik bir yöntem çağırabilirsiniz, obj1.staticMethod()
ancak bu gerçekten sözdizimsel şeker için MyClass.staticMethod()
kaçınılmalıdır. Genellikle modern IDE'de bir uyarı verir. Bu kısayola neden izin verdiklerini bilmiyorum.
clazz2 instanceof clazz1
düzgün yerine kullanabilirsiniz class2.isAssignableFrom(clazz1)
senin örnekte gerçek döneceğini düşünüyoruz, hangi.
Yöntem geçersiz kılma, dinamik gönderme ile mümkün olur , yani bir nesnenin bildirilen türü davranışını değil, çalışma zamanı türünü belirler:
Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"
Her ikisi de lassie
ve kermit
tür nesnesi olarak bildirilse de Animal
, davranışları (yöntemi .speak()
) değişir çünkü dinamik gönderme , yöntem çağrısını derleme zamanında değil, yalnızca çalışma zamanında bir uygulamaya bağlar.speak()
.
Şimdi, burada static
anahtar kelime anlamlı olmaya başlar: "statik" kelimesi "dinamik" için bir zıttır. Bu nedenle, statik yöntemleri geçersiz kılmamanızın nedeni, statik üyelerde dinamik gönderme olmamasıdır - çünkü statik kelimenin tam anlamıyla "dinamik değil" anlamına gelir. Dinamik olarak gönderildiyse (ve dolayısıyla geçersiz kılınabilirlerse), static
anahtar kelime artık bir anlam ifade etmeyecektir.
Evet. Pratik olarak Java statik yöntemi geçersiz kılmaya izin verir ve Java'da statik bir yöntemi geçersiz kılarsanız teorik olarak Hayır derler ve sorunsuz çalışır, ancak Java'nın temel özelliği olan Polimorfizmi kaybeder. Kendinizi derlemeye ve çalıştırmaya çalışmanın mümkün olmadığını Her Yerde okuyacaksınız. cevabını alacaksın. Örneğin, Sınıf Hayvanınız varsa ve statik bir yöntem eat () ise ve Alt Sınıfındaki bu statik yöntemi Geçersiz kılsanız, Köpek olarak adlandıralım. O zaman bir Hayvan Referansı'na bir Köpek nesnesi Atadığınızda ve Java Köpeğinin yiyeceğine () göre eat () çağrıldığında çağrılmalı, ancak statik Geçersiz Kılan Hayvanların yiyeceği () çağrılmalıdır.
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'nın Polimorfizm İlkesine göre Çıktı olmalıdır Dog Eating
.
Ancak sonuç farklıydı çünkü Polimorfizmi desteklemek için Java, Geç Bağlama'yı kullanır; bu, yöntemlerin yalnızca çalışma zamanında çağrıldığı anlamına gelir, ancak statik yöntemler durumunda değil. Statik yöntemlerde derleyici yöntemleri çalışma zamanı yerine derleme zamanında çağırır, bu nedenle nesneye göre bir referans içeren bir referansa değil, referansa göre yöntemler alırız. 't.
geçersiz kılma, örneğin polimorfik davranışı desteklemek için üyeler için ayrılmıştır. statik sınıf üyeleri belirli bir örneğe ait değildir. bunun yerine, statik üyeler sınıfa aittir ve sonuç olarak geçersiz kılma desteklenmez, çünkü alt sınıflar statik üyeleri değil yalnızca korumalı ve genel yönetim ortamı üyelerini devralır. Alternatif bir yaklaşımı değerlendirmek için bir arayüz ve araştırma fabrikası ve / veya strateji tasarım modelleri tanımlamak isteyebilirsiniz.
Java'da (ve birçok OOP dilinde, ancak herkes için konuşamıyorum; ve bazılarında hiç statik yok) tüm yöntemlerin sabit bir imzası var - parametreler ve türler. Sanal bir yöntemde ilk parametre ima edilir: nesnenin kendisine bir başvuru ve nesnenin içinden çağrıldığında derleyici otomatik olarak ekler this
.
Statik yöntemler için bir fark yoktur - hala sabit bir imzası vardır. Ancak, statik yöntemi bildirerek, derleyicinin bu imzanın başına ima edilen nesne parametresini içermemesi gerektiğini açıkça belirtmişsinizdir. Bu nedenle, bunu çağıran diğer kodlar yığındaki bir nesneye başvuru yapmaya çalışmamalıdır . Eğer bunu yaparsa, parametreler yığın üzerinde yanlış yerde - birer birer kaydırılır - olacağından, yöntem yürütme çalışmaz.
İkisi arasındaki bu farktan dolayı; sanal yöntemlerin her zaman bağlam nesnesine (yani this
) bir referansı vardır, bu nedenle öbek içinde nesnenin o örneğine ait olan herhangi bir şeye başvurmak mümkündür. Ancak statik yöntemlerle, başvuru geçmediği için, bağlam bilinmediği için bu yöntem herhangi bir nesne değişkenine ve yönteme erişemez.
Java'nın tanımı, statik veya sanal her yöntem için bir nesne bağlamının geçeceği şekilde değiştirmesini isterseniz, aslında yalnızca sanal yöntemlere sahip olursunuz.
Birisi op'a bir yorumda sorduğu gibi - bu özelliği istemenin sebebi ve amacı nedir?
Ruby'yi pek bilmiyorum, bu OP tarafından belirtildiği gibi, biraz araştırma yaptım. Ruby sınıflarında gerçekten özel bir nesne türü olduğunu ve (hatta dinamik olarak) yeni yöntemler oluşturabildiğini görüyorum. Sınıflar Ruby'deki tam sınıf nesnelerdir, Java'da değildirler. Bu, Java (veya C #) ile çalışırken kabul etmeniz gereken bir şeydir. Bunlar dinamik diller değil, ancak C # bazı dinamik formlar ekliyor. Gerçekte, Ruby bulabildiğim kadarıyla "statik" yöntemlere sahip değil - bu durumda bunlar singleton sınıfı nesnesindeki yöntemler. Daha sonra bu singletonu yeni bir sınıfla geçersiz kılabilirsiniz ve önceki sınıf nesnesindeki yöntemler yeni sınıfta tanımlananları çağırır (doğru?). Dolayısıyla, orijinal sınıf bağlamında bir yöntem çağırdıysanız, yine de yalnızca orijinal statikleri yürütür, ancak türetilmiş sınıfta bir yöntem çağrıldığında, üst veya alt sınıftan yöntemler çağrılır. İlginç ve bunda bir değer görebiliyorum. Farklı bir düşünce modeli gerektirir.
Java'da çalıştığınız için, bu şekilde bir şeyler yapmanız gerekir. Bunu neden yaptılar? Muhtemelen mevcut teknolojiyi ve anlayışı temel alarak o zamanki performansı artırmak. Bilgisayar dilleri sürekli gelişmektedir. Yeterince geriye gidin ve OOP diye bir şey yoktur. Gelecekte başka yeni fikirler olacak.
EDIT : Bir yorum daha. Şimdi farkları görüyorum ve kendimi Java / C # geliştiricisi olarak, Ruby gibi bir dilden geliyorsanız Java geliştiricilerinden aldığınız cevapların neden kafa karıştırıcı olabileceğini anlayabiliyorum. Java static
yöntemleri Ruby class
yöntemleriyle aynı değildir . Java geliştiricileri, çoğunlukla Ruby / Smalltalk gibi bir dille çalışanların aksine, bunu anlamakta zorlanacaklar. Java'nın statik yöntemler hakkında konuşmanın başka bir yolu olarak "sınıf yöntemini" kullanması da bunun nasıl kafa karıştırıcı olabileceğini görebiliyorum, ancak aynı terim Ruby tarafından farklı şekilde kullanılıyor. Java'nın Ruby stili sınıf yöntemleri yoktur (üzgünüm); Ruby, C'de bulunan gerçekten eski prosedürel stil fonksiyonları olan Java tarzı statik yöntemlere sahip değildir.
Bu arada - soru için teşekkürler! Bugün sınıf yöntemleri (Ruby stili) hakkında yeni bir şey öğrendim.
Eh ... Geçersiz bir yöntemin Java'da nasıl davranması gerektiği perspektifinden düşünürseniz cevap HAYIR. Ancak, statik bir yöntemi geçersiz kılmaya çalışırsanız herhangi bir derleyici hatası almazsınız. Bu, geçersiz kılmaya çalışırsanız, Java bunu yapmanıza engel olmaz; ancak statik olmayan yöntemlerle elde ettiğiniz etkiyi kesinlikle elde edemezsiniz. Java'da geçersiz kılma, belirli yöntemin, nesnenin çalışma zamanı türüne göre çağrılması anlamına gelir, derleme zamanı türüne değil (geçersiz kılınmış statik yöntemlerde olduğu gibi). Tamam ... neden garip davrandıklarına dair herhangi bir tahmin var mı? Sınıf metodları olduklarından ve bunlara erişim derleme zamanı boyunca daima derleme zamanı türü bilgileri kullanılarak çözümlendiğinden.
Örnek : Statik bir yöntemi geçersiz kılmaya çalışırsak ne olacağını görmeye çalışalım: -
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();
// ...
}
}
Çıktı : -
SuperClass: inside staticMethod
SuperClass: inside staticMethod
SubClass: inside staticMethod
Çıktının ikinci satırına dikkat edin. StaticMethod geçersiz kılınmış olsaydı, bu satır, 'SuperClass' olarak değil, Çalışma Zamanı Türü'nün bir nesnesindeki 'staticMethod ()' u çağırdığımız için üçüncü satırla aynı olmalıdır. Bu, statik yöntemlerin her zaman yalnızca derleme zamanı türü bilgileri kullanılarak çözümlendiğini doğrular.
Genel olarak statik yöntemleri 'geçersiz kılmaya' izin vermek mantıklı değildir, çünkü çalışma zamanında hangisini arayacağınızı belirlemenin iyi bir yolu yoktur. Çalışan örneğini alırsak, RegularEmployee.getBonusMultiplier () öğesini çağırırsak - hangi yöntemin yürütülmesi gerekir?
Java söz konusu olduğunda, bir nesne örneği aracılığıyla çağrıldıkları sürece statik yöntemleri 'geçersiz kılmanın' mümkün olduğu bir dil tanımı düşünülebilir. Bununla birlikte, tüm yapmanız gereken, gerçekten herhangi bir fayda sağlamadan dile fazlalık ekleyerek düzenli sınıf yöntemlerini yeniden uygulamaktır.
Geçersiz kılarak, nesne türüne bağlı olarak polimorfik bir doğa oluşturabiliriz. Statik yöntemin nesne ile ilişkisi yoktur. Böylece java statik yöntem geçersiz kılmayı destekleyemez.
Jay'in yorumunu beğendim ve ikiye katladım ( https://stackoverflow.com/a/2223803/1517187 ).
Bunun Java'nın kötü tasarımı olduğuna katılıyorum.
Diğer birçok dil, önceki yorumlarda gördüğümüz gibi, statik yöntemlerin geçersiz kılınmasını desteklemektedir. Jay'in benim gibi Delphi'den Java'ya geldiğini hissediyorum.
Delphi (Object Pascal) OOP uygulayan ilk dildi.
Geçmişte ticari GUI ürünleri yazmanın tek dili olduğu için birçok insanın bu dille deneyimi olduğu açıktır. Ve - evet, Delphi'de statik yöntemleri geçersiz kılabiliriz. Aslında, Delphi'deki statik yöntemlere "sınıf yöntemleri" denirken, Delphi'nin "Bağlanma statik yöntemleri" kavramı farklıydı ve bunlar erken bağlanma yöntemiydi. Geç bağlama yöntemlerini geçersiz kılmak için "sanal" yönerge bildirmeniz gerekir. Bu yüzden çok rahat ve sezgisel ve Java bunu beklenebilir.
Statik yöntemleri geçersiz kılmak ne işe yarar? Bir örnek üzerinden statik yöntemleri çağıramazsınız.
MyClass.static1()
MySubClass.static1() // If you overrode, you have to call it through MySubClass anyway.
DÜZENLEME: dil tasarımında talihsiz dikkatsizlik sonucu, o görünür olabilir bir örneği üzerinden statik yöntemleri çağırmak. Genellikle bunu kimse yapmaz. Benim hatam.
variable.staticMethod()
yerine, Class.staticMethod()
nerede, variable
açıklanmış türe sahip bir değişkendir Class
. Kötü dil tasarımı olduğuna katılıyorum.
Java'da geçersiz kılma, belirli yöntemin nesnenin çalışma zamanı türüne göre çağrılması anlamına gelir, derleme zamanı türüne değil (geçersiz kılınmış statik yöntemlerde olduğu gibi). Statik yöntemler sınıf yöntemleri olduğundan, örnek yöntemler değildir, bu nedenle hangi başvurunun hangi Object veya örneğe işaret ettiği gerçeğiyle ilgisi yoktur, çünkü statik yöntemin doğası nedeniyle belirli bir sınıfa aittir. Alt sınıfta yeniden bildirebilirsiniz, ancak alt sınıf, üst sınıfın statik yöntemleri hakkında hiçbir şey bilmeyecektir, çünkü dediğim gibi, yalnızca bildirildiği sınıfa özgüdür. Nesne referanslarını kullanarak onlara erişmek sadece Java tasarımcıları tarafından verilen ekstra bir özgürlüktür ve kesinlikle bu uygulamayı sadece daha fazla ayrıntı ve örnekle kısıtladıklarında durdurmayı düşünmemeliyiz. http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html
Geçersiz kılarak dinamik polimorfizm elde edersiniz. Statik yöntemleri geçersiz kılma dediğinizde, kullanmaya çalıştığınız kelimeler çelişkilidir.
Statik diyor - derleme zamanı, geçersiz kılma dinamik polimorfizm için kullanılır. Her ikisi de doğada zıttır ve bu nedenle birlikte kullanılamaz.
Dinamik polimorfik davranış, bir programcı bir nesne kullandığında ve bir örnek yöntemine eriştiğinde ortaya çıkar. JRE, ne tür bir nesne kullandığınıza bağlı olarak farklı sınıfların farklı örnek yöntemlerini eşler.
Statik yöntemleri geçersiz kıldığınızı söylediğinizde, derleme zamanında bağlanacak olan sınıf adını kullanarak erişeceğimiz statik yöntemler olduğundan, çalışma zamanında yöntemleri statik yöntemlerle bağlama kavramı yoktur. Dolayısıyla statik yöntemlerin "geçersiz kılınması" teriminin bir anlamı yoktur.
Not: bir nesneyle bir sınıf yöntemine erişseniz bile, hala java derleyici bunu bulmak için yeterince akıllıdır ve statik bağlantı yapar.
Box.createBox
daha mantıklıdır BoxFactory.createBox
ve istisnaları atmadan inşaatı kontrol etmek gerektiğinde kaçınılmaz bir modeldir (inşaatçılar başarısız olamaz, sadece işlemi / fırlatmayı öldürebilirler) istisna), bununla birlikte statik bir yöntem hata durumunda null değerini döndürebilir, hatta hastebin.com/codajahati.java gibi bir şey yazmak için başarı / hata geri çağrılarını kabul edebilir .
Bu sorunun cevabı basittir, statik olarak işaretlenen yöntem veya değişken yalnızca sınıfa aittir, Bu nedenle bu statik yöntem yalnızca süper sınıfa ait olduğu için alt sınıfta devralınamaz.
Kolay çözüm: Singleton örneğini kullanın. Geçersiz kılmalara ve mirasa izin verir.
Sistemimde, iletilen Class için örnek döndüren SingletonsRegistry sınıfına sahibim. Örnek bulunmazsa oluşturulur.
Haxe dil sınıfı:
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()
. Kayıt defteri sadece kaynatma tavanıdır ve sınıflarda GC'yi engeller.
Statik bir yöntem, değişken, blok veya iç içe sınıf , bir nesne yerine tüm sınıfa aittir .
Java'da Yöntem, bir Nesnenin / Sınıfın davranışını ortaya çıkarmak için kullanılır. Burada, yöntem statik olduğu için (yani, sadece bir sınıfın davranışını temsil etmek için statik yöntem kullanılır.) Tüm sınıfın davranışını değiştirmek / geçersiz kılmak , Nesne yönelimli programlamanın temel sütunlarından birinin olgusunu, yani yüksek bütünlüğü ihlal eder. . (bir kurucunun Java'da özel bir yöntem olduğunu unutmayın.)
Yüksek Uyum - Bir sınıfın yalnızca bir rolü olmalıdır. Örneğin: Bir otomobil sınıfı, bisiklet, kamyonlar, uçaklar vb. Değil yalnızca otomobil nesneleri üretmelidir. Ancak Car sınıfı, yalnızca kendisine ait bazı özelliklere (davranış) sahip olabilir.
Bu nedenle, java programlama dilini tasarlarken. Dil tasarımcıları, geliştiricilerin yalnızca bir yöntemi doğada statik hale getirerek bir sınıfın bazı davranışlarını kendi başına tutmasına izin vermeyi düşündüler.
Parçalı kod denemesi altında statik yöntemini geçersiz kılmak için, ancak değil herhangi derleme hatayla karşılaşabilirsiniz.
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;
}}
Bunun nedeni, burada bir yöntemi geçersiz kılmıyoruz, ancak sadece yeniden beyan ediyoruz. Java, bir yöntemin (statik / statik olmayan) yeniden bildirilmesine izin verir.
Statik anahtar kelimenin Car sınıfının getVehileNumber () yönteminden kaldırılması derleme hatasına neden olur. Çünkü, yalnızca Vehicle sınıfına ait statik yöntemin işlevselliğini değiştirmeye çalışıyoruz .
Ayrıca, getVehileNumber () son olarak bildirilirse , kod derlenmez, çünkü nihai anahtar kelime programcının yöntemi yeniden bildirmesini kısıtlar.
public static final int getVehileNumber() {
return VIN; }
Genel olarak, bu statik yöntemlerin nerede kullanılacağı için yazılım tasarımcıları kadar. Ben şahsen herhangi bir sınıf örneği oluşturmadan bazı eylemleri gerçekleştirmek için statik yöntemleri kullanmayı tercih ederim. İkincisi, bir sınıfın davranışını dış dünyadan gizlemek.
İşte basit bir açıklama. Statik bir yöntem bir sınıfla ilişkilendirilirken, bir örnek yöntemi belirli bir nesneyle ilişkilendirilir. Geçersiz kılmalar, belirli bir nesneyle ilişkili geçersiz kılınmış yöntemlerin farklı uygulamalarının çağrılmasına izin verir. Bu nedenle, nesnelerle bile ilişkili olmayan, ancak sınıfın kendisinde ilk etapta statik yöntemi geçersiz kılmak tersine sezgiseldir. Statik yöntemler, hangi nesnenin çağırdığını temel alarak geçersiz kılınamaz, her zaman oluşturulduğu sınıfla ilişkilendirilir.
public abstract IBox createBox();
İç IBox arayüzüne sahip olmak nasıl sezgiseldir ? Box, createBox'ı geçersiz kılmak için IBox'ı uygulayabilir ve nesne oluşturmanın geçerli bir IBox ile sonuçlanmasını sağlayabilir, aksi takdirde null değerini döndürür. Yapıcılar "null" döndüremezler ve bu nedenle (1) HER YERDE (şimdi ne yapıyoruz) istisnalar kullanmaya veya (2) daha önce söylediğim şeyi yapan, ancak acemilere veya uzmanlara mantıklı olmayan bir şekilde fabrika sınıfları oluşturmaya zorlanırsınız. Java (şimdi de yapıyoruz). Statik uygulanmayan yöntemler bunu çözer.
Şimdi yukarıdaki cevapları görmek, herkes statik yöntemleri geçersiz kılamayacağımızı bilir, ancak statik sınıflara alt sınıftan erişme kavramı hakkında yanlış anlaşılmamalıdır .
Bu statik yöntem alt sınıfta tanımlanan yeni statik yöntemle gizlenmemişse, alt sınıf başvurusu ile süper sınıf statik yöntemlerine erişebiliriz.
Örneğin, aşağıdaki koda bakın: -
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");
// }
}
Çıktı:-
SuperClass saying Hello
Java oracle belgeleri konusuna bakın ve alt sınıftaki statik yöntemlerin gizlenmesi hakkında ayrıntılar için bir Alt Sınıfta Yapabilecekleriniz'i arayın .
Teşekkürler
Aşağıdaki kod mümkün olduğunu gösterir:
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
olduğu OverridenStaticMeth
değil OverrideStaticMeth
.