Özel yardımcı yöntemlerin statik olması durumunda statik olması gerekir


205

Diyelim ki örneklenecek şekilde tasarlanmış bir sınıfım var. Sınıf içinde herhangi bir sınıf üyelerine erişim gerektirmeyen ve yalnızca kendi argümanları üzerinde çalışan bir sonuç döndüren birkaç özel "yardımcı" yöntemleri var.

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne(member);
       total += computeMore(member);
       return total;         
   }

   private double computeOne(Something arg) { ... }
   private double computeMore(Something arg) {... } 
} 

Belirtmek için belirli bir nedeni var mı computeOneve computeMoreya herhangi özel bir nedeni - statik yöntemler olarak?

Kesinlikle herhangi bir soruna neden olmadan statik olabilmelerine rağmen, statik olmayan olarak bırakmak en kolay yoldur.


3
Ayrıca, Java'da statik anahtar kelimesinin ne zaman kullanılmadığına da bakın: stackoverflow.com/questions/1766715/…
Mark Butler

Yanıtlar:


174

Böyle yardımcı yöntemlerin olmasını tercih ediyorum private static; bu da okuyucuya nesnenin durumunu değiştirmeyeceklerini açıklar. Benim IDE italik statik yöntemlere çağrıları gösterecektir, bu yüzden yöntem imza bakmadan statik olduğunu biliyorum.


2
En sevdiğim iş parçacıklarından biri. NetBeans kullanıyorum ve italik yazı tipleriyle statik yöntem çağrıları gösteriyor.
skiabox

2
Normalde bu yardımcı yöntemleri protected static, aynı paketteki bir test sınıfında çok kolay bir şekilde test edilebilir hale getirdim .
James

2
@James veya basitçe static(sadece test için istiyorsak).
Mart'taki Mrod

3
"Nesnenin durumunu değiştirmez" -Özel bir yöntemden nasıl ayırt edileceğinin mükemmel açıklaması.
HopeKing

110

Statik yöntemlere erişilemediğinden biraz daha küçük bayt kodu ile sonuçlanabilir this. Hızda herhangi bir fark yarattığını sanmıyorum (ve eğer olsaydı, genel olarak bir fark yaratmak için çok küçük olurdu).

Onları statik hale getirirdim, çünkü genellikle mümkünse yaparım. Ama bu sadece benim.


DÜZENLEME: Bu yanıt, muhtemelen bayt kodu boyutu hakkındaki asılsız iddia nedeniyle reddediliyor. Bu yüzden aslında bir test yapacağım.

class TestBytecodeSize {
    private void doSomething(int arg) { }
    private static void doSomethingStatic(int arg) { }
    public static void main(String[] args) {
        // do it twice both ways
        doSomethingStatic(0);
        doSomethingStatic(0);
        TestBytecodeSize t = new TestBytecodeSize();
        t.doSomething(0);
        t.doSomething(0);
    }
}

Bayt kodu (ile alındı javap -c -private TestBytecodeSize):

Compiled from "TestBytecodeSize.java"
class TestBytecodeSize extends java.lang.Object{
TestBytecodeSize();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

private void doSomething(int);
  Code:
   0:   return

private static void doSomethingStatic(int);
  Code:
   0:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   invokestatic    #2; //Method doSomethingStatic:(I)V
   4:   iconst_0
   5:   invokestatic    #2; //Method doSomethingStatic:(I)V
   8:   new     #3; //class TestBytecodeSize
   11:  dup
   12:  invokespecial   #4; //Method "<init>":()V
   15:  astore_1
   16:  aload_1
   17:  iconst_0
   18:  invokespecial   #5; //Method doSomething:(I)V
   21:  aload_1
   22:  iconst_0
   23:  invokespecial   #5; //Method doSomething:(I)V
   26:  return

}

Statik yöntemi çağırmak iki bayt kodu (byteops?) Alır: iconst_0(bağımsız değişken için) ve invokestatic.
Statik olmayan yöntemi çağırmak üç zaman alır: aload_1( TestBytecodeSizesanırım nesne için ), iconst_0(argüman için) ve invokespecial. (Bunlar özel yöntemler olmasaydı, invokevirtualbunun yerine olurdu invokespecial; bkz. JLS §7.7 Çağırma Yöntemleri .)

Şimdi, dediğim gibi, bu iki arasında performans açısından büyük bir fark olmasını beklemiyorum invokestatic. invokestaticve invokespecialher ikisi de biraz daha hızlı olmalı invokevirtual, çünkü ikisi de dinamik yerine statik bağlamayı kullanıyor, ancak ikisinden de daha hızlı olup olmadığı hakkında hiçbir fikrim yok. İyi referanslar da bulamıyorum. Bulabileceğim yakın olan bu 1997 JavaWorld makale temelde ne dediğimi restates:

En hızlı talimatlar büyük olasılıkla olacaktır invokespecialve invokestaticçünkü bu talimatlar tarafından çağrılan yöntemler statik olarak bağlıdır. JVM bu talimatlar için sembolik referansı çözdüğünde ve doğrudan bir referansla değiştirdiğinde, bu doğrudan referans muhtemelen gerçek bayt kodlarına bir işaretçi içerecektir.

Fakat 1997'den beri birçok şey değişti.

Sonuç olarak ... Sanırım daha önce söylediklerime sadık kalıyorum. En iyisi bir mikro optimizasyon olacağından, hız birbirini seçmenin nedeni olmamalıdır.


tüm bu olumsuz oylar çok basit bir cevap gibi görünen şeylere. Doğruluk, adalet, + 1'i + 1
Steve B.

Teşekkürler. (Ben silmiş ve -3 iken bir rozet almış olabilir rağmen.: D)
Michael Myers

2
JIT derleyici yine de bunları satır içi ve optimize edecek, bu nedenle bayt kodu talimatlarının miktarının ne kadar hızlı olacağıyla çok az ilgisi var.
Esko Luontola

3
Bu yüzden "Hızda herhangi bir fark yarattığını sanmıyorum (ve eğer olsaydı, genel olarak bir fark yaratmak için çok küçük olurdu)" dedim.
Michael Myers

7
Bu tür herhangi bir mikro optimizasyona başlamadan önce, sıcak nokta derleyicisinin etkisini düşünmelisiniz. Alt satır: insanlar tarafından okunabilirlik için kodunuzu yazın ve derleyiciye optimizasyon bırakın
Ichthyo

18

Kişisel tercihim, onları vatansız oldukları açık bir bayrak olduğu için statik olarak beyan etmek olacaktır.


18

Cevap, duruma bağlı.

Üye, uğraştığınız nesneye özgü bir örnek değişkeni ise neden parametre olarak iletilsin?

Örneğin:

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne();
       total += computeMore();
       return total;         
   }

   private double computeOne() { /* Process member here */ }
   private double computeMore() { /* Process member here */ } 
}

5
+1: Örnek kötü olsa da, statik olabilen çok fazla yöntem kötü bir kod kokusudur. Bu arada, statik yöntemler aslında funcitons, onlar hiç OO değil. Başka bir sınıfta yöntem olarak olabilirler veya bu işlevin bir yöntem olarak ait olabileceği bir sınıfı kaçırıyor olabilirsiniz.
Bill K

11

Statik yardımcı yöntemleri bildirmek istemenizin bir nedeni, bunları "önce" thisveya sınıf yapıcısında çağırmanız gerektiğidir super. Örneğin:

public class MyClass extends SomeOtherClass { 
    public MyClass(String arg) {
       super(recoverInt(arg));
    }

    private static int recoverInt(String arg) {
       return Integer.parseInt(arg.substring(arg.length() - 1));
    }
}

Bu biraz recoverInttartışmalı bir örnektir, ancak bu durumda açıkça bir örnek yöntem olamaz.


kurucuda bir örnek yöntemini çağırabilirsiniz, ancak super (...) veya this (...) ile başka bir kurucuya temsilci veriyorsanız, yetki verildikten sonraya kadar örnek yöntemini çağıramazsınız (ancak statikler ok belirttiğiniz gibi)
Kip

10

Özel statik yöntem için gerçekten net avantajlar düşünemiyorum. Bununla birlikte, onları statik olmayan hale getirmenin belirli bir avantajı yoktur. Esas olarak bir sunum meselesidir: bir nesneyi değiştirmedikleri gerçeğinin altını çizmek için onları statik hale getirmek isteyebilirsiniz.

Farklı erişim ayrıcalıklarına sahip yöntem için bence iki ana argüman var:

  • statik yöntemler, yararlı olabilecek bir nesnenin örneği oluşturmadan çağrılabilir
  • statik yöntemler miras alınamaz, bu da polimorfizme ihtiyacınız varsa bir sorun olabilir (ancak özel yöntemler için önemsizdir).

Bunun yanı sıra, fark oldukça küçüktür ve bu işaretçinin örnek yöntemine ilettiği fazlalığın önemli bir fark yarattığından şüpheliyim.


4
Statik yapmamanın avantajı, birisinin yöntemin davranışını alt sınıflandırabilmesi ve geçersiz kılabilmesidir.
Clint Miller

7
Özel bir yöntemle Clint yapın, böylece yöntemi geçersiz kılamazsınız.
Bhushan Bhangale

Tam olarak, özel yöntem için bunun çok fazla fark yarattığını düşünmüyorum. Ancak genel yöntemler için söylediklerinize katılıyorum
Axelle Ziegler

8

Doğru Yanıt:

bir alandan herhangi bir bilgi almayan ve bir alana herhangi bir bilgi koymayan herhangi bir yöntemin bir örnek yöntemi olması gerekmez. Sınıfındaki veya nesnesindeki herhangi bir alanı kullanmayan veya değiştirmeyen herhangi bir yöntem de statik bir yöntem olabilir.


5

veya [statik olarak beyan etmemek] için özel bir neden?

Evet.

Bunları örnek yöntemler olarak tutarak, daha sonra farklı bir uygulama sağlamanıza izin verirsiniz.

Aptalca gelebilir (ve aslında bu yöntemler sadece sizin tarafınızdan 50 satırlı bir programda kullanılıyorsa), ancak daha büyük uygulamalarda veya başka biri tarafından kullanılan kütüphanelerde, daha iyi bir uygulama seçmeye karar verebilirsiniz, ancak mevcut kodu kırmak istiyorum.

Böylece bir alt sınıf oluşturursunuz ve bunu yeni sürümlerde döndürürsünüz ve yöntemler örnek yöntemler olarak bildirildiğinden, polimorfizmin işini yapmasına izin verirsiniz.

Ek olarak, yapıcıyı özel yapmaktan ve aynı nedenden dolayı statik bir fabrika yöntemi sağlamaktan faydalanabilirsiniz.

Bu nedenle, tavsiyem onları örnek yöntem olarak tutmak ve mümkünse statikten kaçınmaktır.
Dilin sağladığı dinamizmden yararlanın.

Biraz alakalı bir video için buraya bakın: İyi bir API nasıl tasarlanır ve neden önemlidir?

Doğrudan "statik vs örnek" yöntem tartışması ile ilgili olmasa da, API tasarımında bazı ilginç noktalara değinmektedir.


1
Kesinlikle katılmak. Genellikle bu nedenle statikten ve ayrıcalıklardan tamamen kaçınmaya çalışırım. Diğer cevapların çoğunun bu noktayı kaçırdığını düşünüyorum.
Clint Miller

22
Özel yardımcılardan bahsediyoruz , yani sınıfın herkese açık API'sının bir parçası değiller. Bu, hiçbir şeyin daha sonra farklı bir uygulama sağlamanızı, statik olmayan hale getirmenizi veya başka değişiklikler yapmanızı engellemediği anlamına gelir.
Jonik

Aaaahmm ... bu iyi bir nokta Jonik. Yine de, iyi bir alışkanlık yaratmak yeterli bir sebep olmalıdır (elbette öznel olarak)
OscarRyz

2
Kesinlikle katılmıyorum. Özel bir yöntemi tanımlandığı sınıfı değiştirmeden değiştirmek istemenizin iyi bir nedeni yoktur. Önerdiğini yapmanın tek yolu bayt kodu manipülasyonudur.
Peter Lawrey

2
Yöntemin özel olmasa bile önerdiğiniz şey mantıklı olabilir, ancak o zaman bile hayal edilen bir ihtiyaca göre tasarlamak yerine gerekli bir akıma dayalı tasarım yapmanızı öneririm.
Peter Lawrey

5

Statik yöntemlere sahip olmanın bir sorunu, nesnenin birim testlerde kullanımını daha zor hale getirebilmesidir . Mockito statik yöntemler için alay oluşturamaz ve yöntemin bir alt sınıf uygulamasını oluşturamazsınız.


2
Özel yöntemler için testler yazmamalısınız. Buraya bakın . Statik olsun ya da olmasın özel bir yöntemi test etmeye gerek yoktur.
BW

@BW Bu çok öznel bir şey. Özel bir yöntemi test etmenin birçok geçerli nedeni vardır.
ruohola

4

Yöntem temel olarak yalnızca önceden öngörülemeyecek bir şekilde durum bilgisini kullanmayacak bir alt yordamsa, statik olarak bildirin.

Bu, diğer statik yöntemlerde veya sınıf başlatmada kullanılmasına izin verir, yani:

public class Example {
   //...

   //Only possible if computeOne is static
   public final static double COMPUTED_ONE = computeOne(new Something("1"));

   //...
}

3

Statik / statik olmayan soru, "gerçekten bu sınıftaki bir nesneyi kullanmam gerekecek mi?"

Peki, nesneyi farklı yöntemler arasında geçiriyor musunuz? Nesne, statik yöntemlerin bağlamı dışında yararlı bilgiler içeriyor mu? Her iki şekilde de kullanırsanız yöntemleri her iki şekilde tanımlamamanın bir nedeni var mı?

Bu ikilem içindeyseniz, kodunuzda nesnenin dışında yüzen yöntem için gereken tüm verilere sahip olduğunuzu düşünüyorum. İstediğiniz bu mu? Her seferinde bu verileri her zaman bir nesneye toplamak daha kolay olur mu? Tek bir modele bağlılık konusunda kararsız olabilirsiniz. Hepsini tek bir yolla yapabilirseniz, statik veya statik olmayanları seçin ve onunla devam edin.


3

Bu gibi durumlarda benim tercih marka olmaktır computeOneve computeMorestatik yöntemlerle. Nedeni: kapsülleme. Sınıfınızın uygulanmasına erişimi ne kadar az olursa o kadar iyidir.

Verdiğiniz örnekte , sınıfın içlerine erişmeniz gerektiğini computeOneve computeMoregerekmediğini belirtiyorsunuz , öyleyse neden sınıfın sahiplerine içsellerle uğraşma fırsatı verin.


3

Diğer posterlerin yanlış bilgi vermek olarak söylediği birkaç şeyi açıklığa kavuşturmak istiyorum.

İlk olarak, yöntemler statik olarak bildirilse bile özel olduğundan, bu sınıfın dışında bunlara erişemezsiniz. İkincisi, özeldir, bu nedenle alt sınıfta bile geçersiz kılamazsınız, böylece statik veya statik olmayan herhangi bir fark yaratmaz. Üçüncüsü, statik olmayan özel bir yöntem sınıfın bir yapıcısından da çağrılabilir, statik olması gerekmez.

Şimdi özel bir yardımcı yöntemin statik veya statik olmayan olarak tanımlanması gerekip gerekmediği sorunuza geliyor. Özel bir yöntem statik işaretleme olarak ben de bu kodu varken ben bu kuralı takip gibi stateless gösterir Steve'in cevabı ile gidecek.


ancak
örneğimde

Evet Kip'iniz ve Chris telesekreteriniz doğru. Onlara doğru cevaplar olduğu için yorum yapmadım. Sadece yanlış cevaplardan bahsettim.
Bhushan Bhangale

3

Deneyimlerime göre, bu tür özel yöntemlerin oldukça evrensel ve tekrar kullanılabilir olma eğiliminde olduğunu söyleyebilirim.

Bence yapılacak ilk şey yöntemin mevcut sınıf bağlamının dışında faydalı olup olmayacağı sorusunu sormaktır. Eğer öyleyse, herkesin önerdiğini tam olarak yapardım ve bu yöntemi birisinin tam olarak aynı şeyi yapan yeni yöntemi uygulamadan önce kontrol ettiği bazı utils sınıfına statik olarak çıkarın.

Bu tür genel kullanım özel yöntemleri, projedeki kod çoğaltmanın büyük bir kısmının kaynağıdır, çünkü her geliştirici bunları sadece kullanması gereken yerde bağımsız olarak yeniden icat eder. Yani bu tür yöntemlerin merkezileştirilmesi bir yol.


2

Daha spesifik olarak, verdiğiniz örneğe göre, bu yöntemleri tanımlamanın amacı, kodu okurken kod işlevselliği için (işlevsellik olarak tanımlanmıştır) daha fazladır . Bu durumda, statikle gitmek sizin için hiçbir şey yapmaz, çünkü statikin amacı sınıf işlevselliğini ortaya çıkarmaktır.


Başka cevaplar altında gömülü olan bu küçük cevap, konunun özüne gerçekten dokunuyor: sınıf düzeyinde işlevsellik ile nesne düzeyinde işlevsellik.
eljenso

Teşekkürler, el, gerçekten minnettarım. Ben de biraz oy umursamıyorum :)
not

Yorum yazdığım sırada ona +1 verdim.
eljenso

1
Katılmıyorum. Kod açıklığı özel yöntemler için hala önemlidir. Daha az önemli olabilir ama hala çabalamak için bir şey.
LegendLength

1

Bunun bir nedeni, diğer her şeyin eşit olması durumunda, statik yöntem çağrılarının daha hızlı olması gerektiğidir. Statik yöntemler sanal olamaz ve bu başvuruyu örtük almaz.


Aşağıdaki cevabımı görün (Analizi zaten -3 yaşında olduktan sonra ekledim, bu yüzden çok görünür değil).
Michael Myers

1

Birçok insanın dediği gibi, bunu statik olarak yapın ! Takip ettiğim başparmak kuralı: Yöntemin sadece matematiksel bir işlev olduğunu düşünüyorsanız olduğunu, yani durumsuz herhangi bir örnek değişkeni içermez (=> hiçbir mavi renk değişmez [tutulmada]) ve sonucu yöntem 'n' çağrı sayısı için aynı olacaktır (aynı parametrelerle, tabii ki), sonra bu yöntemi STATİK olarak işaretleyin.

Ve bu yöntemin diğer sınıfa yararlı olacağını düşünüyorsanız, bunu bir Util sınıfına taşıyın, yöntemi sameclass'a özel olarak koyun. (erişilebilirliği en aza indirir)


1

Konu Dışı: Yardımcı yöntemleri yalnızca statik yöntemlerle bağımsız bir yardımcı program / yardımcı sınıfta tutardım.

Kullanım noktasında yardımcı yöntemlere sahip olmanın zorluğu ('aynı sınıfı' okuyun) satırın aşağısındaki birisinin sadece kendi ilgisiz yardımcı yöntemlerini aynı yerde yayınlamayı seçebilmesidir.


-1: SO bir mesajlaşma forumu değil. Aslında uygun bir soru sorarak daha iyisini yaparsınız.
Stu Thompson

1
Statik olanlar olarak bağlı / ilişkili sınıf ile birlikte yardımcı yöntemleri olmasını tercih ederim. Dahası, harici olarak kullanılması gerekiyorsa bazılarını herkese açık olarak gösterebilirim. Bu şekilde, herkesin maruz kalması ve yoğun bir şekilde kullanılması gerekiyorsa, söz konusu sınıfın API'sını korumak daha kolay olacaktır.
Ivaylo Slavov

1
class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

Özel statik yöntemlerin avantajı, sınıf değişkenini yeniden başlatmanız gerektiğinde daha sonra yeniden kullanılabilmeleridir.


1
  1. Statik değiştirici olmadan, yöntemi yeniden yazdığınızda kolayca yapılabilen ek analiz olmadan yöntemin durumsuz olduğunu anlayamazsınız.

  2. Sonra "statik" değiştirici, başkalarının yararsız bulabileceği diğer şeylerin yanı sıra yeniden düzenleme hakkında da fikir verebilir. Yöntemi bazı Utility sınıfına taşımak veya üye yönteme dönüştürmek.


0

Onları vatansız olarak işaretlemek için statik olarak beyan ederim.

Java, dışa aktarılmamış küçük işlemler için daha iyi bir mekanizmaya sahip değil, bu yüzden özel statik kabul edilebilir olduğunu düşünüyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.