Apa keuntungan dari mendeklarasikan metode sebagai statis

94

Saya baru-baru ini melihat peringatan saya di Eclipse dan menemukan yang ini:

peringatan statis

Ini akan memberikan peringatan compiler jika metode dapat dideklarasikan sebagai statis.

[sunting] Kutipan yang tepat dalam bantuan Eclipse, dengan penekanan pada pribadi dan final:

Saat diaktifkan, kompilator akan mengeluarkan kesalahan atau peringatan untuk metode yang bersifat pribadi atau final dan yang merujuk hanya ke anggota statis.

Ya, saya tahu saya bisa mematikannya, tapi saya ingin tahu alasan untuk menyalakannya?

Mengapa sebaiknya mendeklarasikan setiap metode yang mungkin sebagai statis?

Apakah ini akan memberikan keuntungan kinerja? (di domain seluler)

Menunjukkan metode sebagai statis, saya kira menunjukkan bahwa Anda tidak menggunakan variabel instan sehingga dapat dipindahkan ke kelas gaya utils?

Di penghujung hari, haruskah saya menonaktifkan 'abaikan' ini atau haruskah saya memperbaiki 100+ peringatan yang diberikannya kepada saya?

Apakah menurut Anda ini hanya kata kunci tambahan yang mengotori kode, karena kompiler hanya akan menyebariskan metode ini? (jenis seperti Anda tidak mendeklarasikan setiap variabel yang Anda bisa final tetapi Anda bisa ).

Blundell
sumber
Tidak yakin tapi ini bisa dilihat sebagai bantuan pemrograman. Peringatan hanyalah indikasi dari hal-hal yang perlu diperhatikan.
James P.
1
Saya penasaran dengan jenis fungsionalitas yang dilakukan metode ini. Mungkin ada sesuatu yang tidak dilakukan dengan benar jika jumlahnya begitu banyak.
ArjunShankar
Terkait: stackoverflow.com/q/790281
Brian Rasmussen

Jawaban:

132

Setiap kali Anda menulis metode, Anda memenuhi kontrak dalam ruang lingkup tertentu. Semakin sempit cakupannya, semakin kecil kemungkinan Anda menulis bug.

Jika sebuah metode statis, Anda tidak dapat mengakses anggota non-statis; karenanya, cakupan Anda lebih sempit. Jadi, jika Anda tidak membutuhkan dan tidak akan pernah membutuhkan (bahkan dalam subclass) anggota non-statis untuk memenuhi kontrak Anda, mengapa memberikan akses ke bidang ini ke metode Anda? Mendeklarasikan metode staticdalam kasus ini akan membiarkan compiler memeriksa bahwa Anda tidak menggunakan anggota yang tidak ingin Anda gunakan.

Dan terlebih lagi, ini akan membantu orang yang membaca kode Anda memahami sifat kontrak.

Itulah mengapa dianggap baik untuk mendeklarasikan metode staticketika itu benar-benar menerapkan kontrak statis.

Dalam beberapa kasus, metode Anda hanya berarti sesuatu yang relatif terhadap sebuah instance kelas Anda, dan itu terjadi bahwa implementasinya tidak benar-benar menggunakan bidang atau instance non-statis. Dalam kasus seperti itu, Anda tidak akan menandai metode tersebut static.

Contoh di mana Anda tidak akan menggunakan statickata kunci:

  • Pengait ekstensi yang tidak melakukan apa pun (tetapi dapat melakukan sesuatu dengan data contoh dalam subkelas)
  • Perilaku default yang sangat sederhana yang dimaksudkan untuk dapat disesuaikan dalam subkelas.
  • Implementasi event handler: implementasi akan bervariasi dengan kelas event handler tetapi tidak akan menggunakan properti apapun dari instance event handler.
Samuel Rossille
sumber
1
+1 Ini tentang meminimalkan peluang untuk menjatuhkan diri sendiri, dan mengurangi seberapa banyak yang perlu Anda ketahui untuk memahami suatu metode.
Peter Lawrey
7
Selain itu, Anda tidak perlu mengutak-atik memperoleh instance hanya untuk memanggil fungsi mandiri dan mandiri.
Marko Topolnik
1
Jadi di dunia lain, metode apa pun yang tidak menggunakan variabel instan harus dideklarasikan sebagai statis? Saya selalu berpikir bahwa metode harus statis hanya jika diinginkan (seperti metode utilitas).
Petr Mensik
18
@PetrMensik Jika sebuah privatemetode dapat dideklarasikan statis, maka hampir selalu harus demikian. Untuk tingkat akses lainnya, ada faktor lain yang perlu dipertimbangkan, seperti pengiriman dinamis.
Marko Topolnik
1
@JamesPoulson Maksud saya pengiriman metode dinamis, hal yang terjadi saat exetucing object.method()memilih metode untuk memanggil.
Marko Topolnik
15

Tidak ada konsep dengan pengoptimalan di sini.

Sebuah staticmetode adalah statickarena Anda secara eksplisit menyatakan bahwa metode tidak bergantung pada setiap contoh kelas melampirkan hanya karena tidak perlu. Demikian peringatan Eclipse, seperti yang tertera dalam dokumentasi:

Saat diaktifkan, kompilator akan mengeluarkan kesalahan atau peringatan untuk metode yang bersifat pribadi atau final dan yang merujuk hanya ke anggota statis.

Jika Anda tidak memerlukan variabel instan dan metode Anda bersifat privat (tidak dapat dipanggil dari luar) atau final (tidak dapat diganti) maka tidak ada alasan untuk membiarkannya menjadi metode normal daripada metode statis. Metode statis secara inheren lebih aman bahkan hanya karena Anda diizinkan untuk melakukan lebih sedikit hal dengannya (tidak memerlukan contoh apa pun, Anda tidak memiliki thisobjek implisit ).

Mendongkrak
sumber
7

Saya tidak memiliki info tentang kinerjanya, saya kira itu paling sedikit lebih baik, karena kode tidak perlu melakukan pengiriman dinamis berdasarkan tipenya.

Namun, argumen yang jauh lebih kuat terhadap pemfaktoran ulang menjadi metode statis adalah bahwa saat ini menggunakan statis dianggap praktik yang buruk. Metode / variabel statis tidak terintegrasi dengan baik ke dalam bahasa berorientasi objek dan juga, sulit untuk diuji dengan benar. Inilah alasan mengapa beberapa bahasa baru mengabaikan konsep metode / variabel statis sama sekali, atau mencoba untuk menginternalisasikannya ke dalam bahasa dengan cara yang lebih baik dalam OO (mis. Objek di Scala).

Sering kali, Anda memerlukan metode statis untuk mengimplementasikan fungsi yang hanya menggunakan parameter sebagai input dan menghasilkan output menggunakan itu (misalnya fungsi utilitas / pembantu) Dalam bahasa modern, ada konsep Fungsi kelas satu yang memungkinkannya, jadi statis tidak dibutuhkan. Java 8 akan memiliki ekspresi lambda terintegrasi, jadi kita sudah bergerak ke arah ini.

Istvan Devai
sumber
7
Metode statis sangat mudah untuk diuji (selama metode tersebut berdiri sendiri - terutama fungsi murni, yang merupakan target utama metode statis). Konsep OO tidak ada yang ditawarkan dalam kasus itu. Juga, konsep fungsi kelas satu memiliki sangat sedikit, jika ada, hubungannya dengan konsep metode statis. Jika Anda pernah membutuhkan fungsi kelas satu yang melakukan pekerjaan metode statis yang ada, itu adalah masalah beberapa karakter untuk mengimplementasikannya.
Marko Topolnik
5
Pernyataan testability mungkin tidak diarahkan ke metode statis secara langsung, tetapi metode statis jauh lebih sulit untuk dipalsukan, sehingga mempersulit pengujian metode yang memanggil metode statis.
Buhb
2
Metode statis mudah diejek jika Anda menggunakan kerangka kerja tiruan yang kuat seperti JMockit , PowerMock , atau Groovy .
Jeff Olson
Ini adalah private staticmetode sehingga Anda tidak perlu mengejeknya
Blundell
3

1. Metode deklarasistaticmemberikan sedikit manfaat kinerja, tetapi yang lebih berguna, ini memungkinkan penggunaan tanpa memiliki instance objek di tangan (pikirkan misalnya tentang metode pabrik atau mendapatkan singleton). Ini juga melayani tujuan dokumentasi untuk menceritakan sifat metode. Tujuan dokumentasi ini tidak boleh diabaikan, karena memberikan petunjuk langsung tentang sifat metode kepada pembaca kode dan pengguna API dan juga berfungsi sebagai alat untuk berpikir bagi programmer asli - menjadi eksplisit tentang makna yang dimaksudkan membantu Anda juga berpikir jernih dan menghasilkan kode kualitas yang lebih baik (menurut saya berdasarkan pengalaman pribadi saya, tetapi orang-orang berbeda). Sebagai contoh, adalah logis dan oleh karena itu diinginkan untuk membedakan antara metode yang beroperasi pada suatu tipe dan metode yang bekerja pada sebuah instance dari tipe tersebut (seperti ditunjukkan olehJon Skeet dalam komentarnya untuk pertanyaan C # ).

Namun kasus penggunaan lain untuk staticmetode adalah untuk meniru antarmuka pemrograman prosedural. Pikirkan java.lang.System.println()kelas dan metode serta atribut di dalamnya. Kelas java.lang.Systemdigunakan seperti ruang nama pengelompokan daripada objek instantiable.

2. Bagaimana Eclipse (atau program lain atau jenis - biocomposable atau non-biocomposable - entity) mengetahui dengan pasti metode mana yang dapat dideklarasikan sebagai statis? Bahkan jika kelas dasar tidak mengakses variabel instan atau memanggil metode non-statis, dengan mekanisme pewarisan hal-hal dapat berubah. Hanya jika metode tersebut tidak dapat diganti dengan mewarisi subkelas, kami dapat mengklaim dengan kepastian 100% bahwa metode tersebut benar-benar dapat dideklarasikan static. Mengesampingkan metode tidak mungkin dilakukan persis dalam dua kasus keberadaan

  1. private (tidak ada subclass yang dapat menggunakannya secara langsung dan bahkan pada prinsipnya tidak mengetahuinya), atau
  2. final (meskipun dapat diakses oleh subclass, tidak ada cara untuk mengubah metode untuk merujuk ke data atau fungsi instance).

Oleh karena itu logika opsi Eclipse.

3. Poster asli juga bertanya: “ Menunjukkan metode sebagai statis, saya kira menunjukkan bahwa Anda tidak menggunakan variabel instan sehingga dapat dipindahkan ke kelas gaya utils? ” Ini adalah poin yang sangat bagus. Terkadang perubahan desain semacam ini ditunjukkan dengan peringatan.

Ini adalah opsi yang sangat berguna, yang secara pribadi akan saya pastikan untuk diaktifkan, jika saya menggunakan Eclipse dan jika saya memprogram di Java.

FooF
sumber
1

Lihat jawaban Samuel tentang bagaimana ruang lingkup metode berubah. Saya rasa, ini adalah aspek utama dalam membuat metode statis.

Anda juga bertanya tentang kinerja:

Mungkin ada keuntungan performa yang kecil, karena panggilan ke metode statis tidak memerlukan referensi implisit "ini" sebagai parameter.

Namun, dampak kinerja ini sangat kecil. Oleh karena itu, ini semua tentang ruang lingkup.

Hitam
sumber
1

Dari pedoman Performa Android:

Lebih Memilih Statis daripada Virtual Jika Anda tidak perlu mengakses bidang objek, buat metode Anda statis. Pemanggilan akan menjadi sekitar 15% -20% lebih cepat. Ini juga praktik yang baik, karena Anda dapat mengetahui dari tanda tangan metode bahwa memanggil metode tersebut tidak dapat mengubah status objek.

http://developer.android.com/training/articles/perf-tips.html#PreferStatic

Blundell
sumber
"bahwa memanggil metode tersebut tidak dapat mengubah status objek" - sangatlah menyesatkan. Saya tidak tahu tentang Anda, tapi saya pasti menganggap atribut statis kelas menjadi bagian dari status objek.
Adam Parkin
0

Dokumentasi Eclipse menjelaskan tentang peringatan yang dimaksud:

Metode bisa statis

Saat diaktifkan, kompilator akan mengeluarkan kesalahan atau peringatan untuk metode yang bersifat pribadi atau final dan yang merujuk hanya ke anggota statis

Saya pikir itu cukup banyak mengatakan itu semua. Jika metode tersebut bersifat pribadi dan final dan hanya merujuk pada anggota statis, metode yang dimaksud mungkin juga akan dinyatakan statis dan dengan ini, jelaskan bahwa kami hanya bermaksud untuk mengakses konten statis darinya.

Sejujurnya saya tidak berpikir ada alasan misterius lain di baliknya.

Edwin Dalorzo
sumber
0

Saya kehilangan beberapa angka untuk perbedaan kecepatan. Jadi saya mencoba untuk melakukan benchmark yang ternyata tidak begitu mudah: Java loop menjadi lebih lambat setelah beberapa run / kesalahan JIT?

Saya akhirnya menggunakan Caliper dan hasilnya sama dengan menjalankan tes saya dengan tangan:

Tidak ada perbedaan terukur untuk panggilan statis / dinamis. Setidaknya tidak untuk Linux / AMD64 / Java7.

Hasil Caliper ada di sini: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPercent,scenari.vm. CMSLargeSplitSurplusPercent, scenario.vmSpec.options.CMSSmallCoalSurplusPercent, scenario.vmSpec.options.CMSSmallSplitSurplusPercent, scenario.vmSpec.options.FLSLargestMarkCoalesceProximity, scenario.vmSpec.options.FLSLargest.vmark

dan hasil saya sendiri adalah:

Static: 352 ms
Dynamic: 353 ms
Static: 348 ms
Dynamic: 349 ms
Static: 349 ms
Dynamic: 348 ms
Static: 349 ms
Dynamic: 344 ms

Kelas Tes Kaliper adalah:

public class TestPerfomanceOfStaticMethodsCaliper extends Benchmark {

    public static void main( String [] args ){

        CaliperMain.main( TestPerfomanceOfStaticMethodsCaliper.class, args );
    }

    public int timeAddDynamic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addDynamic( 1, i );
        }
        return r;
    }

    public int timeAddStatic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addStatic( 1, i );
        }
        return r;
    }

    public int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

Dan kelas Tes saya sendiri adalah:

public class TestPerformanceOfStaticVsDynamicCalls {

    private static final int RUNS = 1_000_000_000;

    public static void main( String [] args ) throws Exception{

        new TestPerformanceOfStaticVsDynamicCalls().run();
    }

    private void run(){

        int r=0;
        long start, end;

        for( int loop = 0; loop<10; loop++ ){

            // Benchmark

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addStatic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Static: " + ( end - start ) + " ms" );

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addDynamic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Dynamic: " + ( end - start ) + " ms" );

            // Do something with r to keep compiler happy
            System.out.println( r );

        }

    }

    private int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}
Scheintod
sumber
akan menarik untuk melihat hasilnya pada berbagai perangkat android dan versi
Blundell
Ya. Saya pikir Anda akan tertarik :) Tetapi karena Anda sedang membangun perangkat lunak android dan mungkin memiliki perangkat android yang terhubung ke stasiun pengembang Anda, saya sarankan Anda cukup memilih kode, menjalankannya dan membagikan hasilnya?
Scheintod
-2

Metode yang dapat Anda deklarasikan sebagai statis adalah metode yang tidak memerlukan pembuatan instance, seperti

public class MyClass
{
    public static string InvertText(string text)
    {
        return text.Invert();
    }
}

Yang kemudian dapat Anda panggil kembali di kelas lain tanpa memberi contoh kelas itu.

public class MyClassTwo
{
    public void DoSomething()
    {
        var text = "hello world";
        Console.Write(MyClass.InvertText(text));
    }
}

... Tapi itu sesuatu yang mungkin sudah Anda ketahui. Itu tidak memberi Anda manfaat nyata apa pun, selain membuatnya lebih jelas bahwa metode tersebut tidak menggunakan variabel instan apa pun.

Dengan kata lain, Anda dapat mematikannya sepenuhnya dengan aman. Jika Anda tahu bahwa Anda tidak akan pernah menggunakan metode di kelas lain (dalam hal ini seharusnya hanya bersifat pribadi), Anda tidak perlu metode itu statis sama sekali.

NeroS
sumber
1
Bahasa apa itu Apakah contoh itu dapat dikompilasi? Tidak ada yang statis di sini.
Piotr Perak
Memang, sepertinya saya lupa 'statis' dari metode InvertText. Dan ini adalah contoh berdasarkan c #
NeroS