Haruskah metode pembantu pribadi bersifat statis jika bisa statis

205

Katakanlah saya memiliki kelas yang dirancang untuk dipakai. Saya memiliki beberapa metode "pembantu" pribadi di dalam kelas yang tidak memerlukan akses ke anggota kelas mana pun, dan beroperasi hanya berdasarkan argumen mereka, mengembalikan hasilnya.

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) {... } 
} 

Apakah ada alasan khusus untuk menentukan computeOnedan computeMoresebagai metode statis - atau alasan tertentu untuk tidak melakukannya?

Tentunya lebih mudah untuk membiarkan mereka sebagai non-statis, meskipun mereka bisa statis tanpa menimbulkan masalah.

avalys
sumber
3
Lihat juga saat tidak menggunakan kata kunci statis di Jawa: stackoverflow.com/questions/1766715/…
Mark Butler

Jawaban:

174

Saya lebih suka metode pembantu seperti itu private static; yang akan menjelaskan kepada pembaca bahwa mereka tidak akan mengubah keadaan objek. IDE saya juga akan menunjukkan panggilan ke metode statis dalam huruf miring, jadi saya akan tahu metode ini statis tanpa melihat tanda tangan.

Esko Luontola
sumber
2
Salah satu utas favorit saya. Saya menggunakan NetBeans dan itu menunjukkan panggilan metode statis dengan font miring.
skiabox
2
Saya biasanya mendeklarasikan metode pembantu protected staticyang membuatnya mudah diuji di kelas uji dalam paket yang sama.
James
2
@James atau sederhananya static(jika kami hanya menginginkannya untuk pengujian).
mrod
3
"Tidak akan mengubah keadaan objek" - penjelasan yang sempurna tentang cara membedakannya dari metode pribadi.
HopeKing
110

Mungkin menghasilkan bytecode sedikit lebih kecil, karena metode statis tidak akan mendapatkan akses this. Saya tidak berpikir itu membuat perbedaan dalam kecepatan (dan jika ya, mungkin akan terlalu kecil untuk membuat perbedaan secara keseluruhan).

Saya akan membuatnya statis, karena saya biasanya melakukannya jika memungkinkan. Tapi itu hanya aku.


EDIT: Jawaban ini terus diturunkan, mungkin karena pernyataan tidak berdasar tentang ukuran bytecode. Jadi saya benar-benar akan menjalankan tes.

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);
    }
}

Bytecode (diambil dengan 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

}

Meminta metode statis membutuhkan dua bytecode (byteops?): iconst_0(Untuk argumen) dan invokestatic.
Meminta metode non-statis membutuhkan tiga: aload_1(untuk TestBytecodeSizeobjek, saya kira), iconst_0(untuk argumen), dan invokespecial. (Perhatikan bahwa jika ini bukan metode pribadi, itu akan menjadi invokevirtualbukan invokespecial; lihat JLS §7.7 Metode Memohon .)

Sekarang, seperti yang saya katakan, saya tidak berharap akan ada perbedaan besar dalam kinerja antara keduanya, selain dari fakta yang invokestaticmembutuhkan satu bytecode lebih sedikit. invokestaticdan invokespecialkeduanya harus sedikit lebih cepat daripada invokevirtual, karena keduanya menggunakan pengikatan statis alih-alih dinamis, tapi saya tidak tahu apakah keduanya lebih cepat dari yang lain. Saya juga tidak dapat menemukan referensi yang bagus. Yang paling dekat yang bisa saya temukan adalah artikel JavaWorld 1997 ini , yang pada dasarnya menyatakan kembali apa yang saya katakan:

Instruksi tercepat kemungkinan besar akan invokespecialdan invokestatic, karena metode yang dipanggil oleh instruksi ini terikat secara statis. Ketika JVM menyelesaikan referensi simbolik untuk instruksi ini dan menggantinya dengan referensi langsung, referensi langsung itu mungkin akan menyertakan pointer ke bytecodes yang sebenarnya.

Tetapi banyak hal telah berubah sejak 1997.

Jadi sebagai kesimpulan ... Saya kira saya masih tetap dengan apa yang saya katakan sebelumnya. Kecepatan tidak boleh menjadi alasan untuk memilih satu dari yang lain, karena itu akan menjadi optimasi mikro terbaik.

Michael Myers
sumber
semua suara negatif ini untuk apa yang tampaknya seperti jawaban yang sangat mudah. +1 untuk kepentingan kebenaran, keadilan, ....
Steve B.
Terima kasih. (Meskipun saya bisa menghapusnya dan mendapatkan lencana saat berada di -3.: D)
Michael Myers
2
Kompiler JIT akan tetap inline dan mengoptimalkannya, sehingga jumlah instruksi bytecode tidak ada hubungannya dengan seberapa cepat akan.
Esko Luontola
3
Itu sebabnya saya berkata, "Saya tidak berpikir itu membuat perbedaan dalam kecepatan (dan jika itu terjadi, mungkin terlalu kecil untuk membuat perbedaan secara keseluruhan)."
Michael Myers
7
Sebelum terlibat dalam optimasi mikro semacam itu, Anda harus mempertimbangkan efek kompiler hot spot. Intinya: tulis kode Anda untuk dibaca oleh manusia dan serahkan pengoptimalan ke kompiler
Ichthyo
18

Preferensi pribadi saya adalah menyatakannya statis, karena ini adalah tanda yang jelas bahwa mereka tidak memiliki kewarganegaraan.

Steve B.
sumber
18

Jawabannya adalah, tergantung.

Jika anggota adalah variabel instan untuk objek yang Anda hadapi, lalu mengapa harus memberikannya sebagai parameter?

Misalnya:

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 */ } 
}
Powerlord
sumber
5
+1: Meskipun contohnya buruk, terlalu banyak metode yang BISA statis adalah bau kode yang buruk. By the way, metode statis sebenarnya adalah funcitons, mereka sama sekali tidak OO. Mereka mungkin termasuk sebagai metode di kelas lain, atau Anda mungkin melewatkan kelas yang fungsi ini bisa dimiliki sebagai metode.
Bill K
11

Salah satu alasan Anda mungkin ingin mendeklarasikan metode pembantu statis adalah jika Anda perlu memanggil mereka di konstruktor kelas "sebelum" thisatau super. Sebagai contoh:

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));
    }
}

Ini sedikit contoh yang dibuat-buat tetapi jelas recoverInttidak bisa menjadi metode contoh dalam kasus ini.

oxbow_lakes
sumber
Anda dapat memanggil metode contoh di konstruktor, tetapi jika Anda mendelegasikan ke konstruktor lain dengan super (...) atau ini (...), Anda tidak bisa memanggil metode contoh hingga setelah delegasi (tetapi statika adalah ok saat Anda menunjukkan)
Kip
10

Saya tidak bisa memikirkan keuntungan yang jelas untuk metode statis pribadi. Yang sedang berkata, tidak ada keuntungan khusus untuk membuat mereka tidak statis juga. Ini terutama masalah presentasi: Anda mungkin ingin membuatnya statis untuk menggarisbawahi fakta bahwa mereka tidak mengubah objek.

Untuk metode dengan hak akses yang berbeda, saya pikir ada dua argumen utama:

  • metode statis dapat dipanggil tanpa membuat turunan objek, yang dapat berguna
  • metode statis tidak dapat diwariskan, yang dapat menjadi masalah jika Anda memerlukan polimorfisme (tetapi tidak relevan untuk metode pribadi).

Selain itu, perbedaannya cukup kecil, dan saya sangat ragu bahwa tambahan pointer ini diteruskan ke metode contoh membuat perbedaan yang signifikan.

Axelle Ziegler
sumber
4
Keuntungan dari tidak menjadikannya statis adalah bahwa seseorang dapat subclass dan mengesampingkan perilaku metode.
Clint Miller
7
Clint dengan metode pribadi sehingga Anda tidak dapat menimpa metode.
Bhushan Bhangale
Tepat, untuk metode pribadi saya tidak berpikir itu membuat banyak perbedaan. Untuk metode publik, bagaimanapun, saya setuju dengan apa yang Anda katakan
Axelle Ziegler
8

Jawaban yang Benar adalah:

metode apa pun yang tidak mengambil informasi apa pun dari suatu bidang, dan tidak memasukkan informasi apa pun ke dalam bidang, tidak harus menjadi metode contoh. Metode apa pun yang tidak menggunakan atau mengubah bidang apa pun di kelas atau objeknya mungkin juga merupakan metode statis.

Gyan Singh
sumber
5

atau alasan tertentu untuk tidak [menyatakannya sebagai statis]?

Iya.

Dengan menjadikannya sebagai metode instan, Anda mengizinkan diri Anda untuk memberikan implementasi yang berbeda nanti.

Mungkin terdengar konyol (dan sebenarnya jika metode itu hanya digunakan oleh Anda dalam program 50 baris) tetapi dalam aplikasi yang lebih besar, atau di perpustakaan yang digunakan oleh orang lain, Anda dapat memutuskan untuk memilih implementasi yang lebih baik, tetapi jangan ingin memecahkan kode yang ada.

Jadi, Anda membuat subkelas dan mengembalikannya di versi baru, dan karena metode tersebut dinyatakan sebagai metode instan, Anda hanya membiarkan polimorfisme melakukan tugasnya.

Selain itu, Anda dapat mengambil manfaat dari menjadikan konstruktor sebagai pribadi dan menyediakan metode pabrik statis untuk alasan yang sama.

Jadi, rekomendasi saya adalah menyimpannya sebagai metode instan, dan menghindari statis jika memungkinkan.
Manfaatkan dinamika yang disediakan bahasa.

Lihat di sini untuk video yang agak terkait: Cara mendesain API yang baik dan mengapa itu penting

Meskipun tidak terkait langsung dengan diskusi metode "static vs instance", itu menyentuh beberapa poin menarik dalam desain API.

OscarRyz
sumber
1
Sangat setuju. Saya biasanya mencoba menghindari statika dan kemaluan sama sekali hanya untuk alasan ini. Saya pikir sebagian besar jawaban lain telah melewatkan intinya.
Clint Miller
22
Kita berbicara tentang pembantu pribadi , yang berarti mereka bukan bagian dari API publik kelas. Itu tidak berarti apa-apa yang mencegah Anda dari memberikan implementasi yang berbeda di kemudian hari, menjadikannya non-statis, atau membuat perubahan lainnya.
Jonik
Aaaahmm ... itu poin bagus Jonik. Namun, menciptakan kebiasaan yang baik harus menjadi alasan yang cukup (tentu saja secara
subyektif
2
Sepenuhnya tidak setuju. Tidak ada alasan bagus Anda ingin mengubah metode pribadi tanpa mengubah kelas yang didefinisikannya. Satu-satunya cara untuk melakukan apa yang Anda sarankan adalah dengan manipulasi byte-code.
Peter Lawrey
2
Apa yang Anda sarankan bisa masuk akal jika metode ini tidak bersifat pribadi, tetapi meskipun demikian saya akan menyarankan Anda mendesain berdasarkan kebutuhan saat ini daripada mendesain berdasarkan kebutuhan yang dibayangkan.
Peter Lawrey
5

Salah satu masalah tentang memiliki metode statis adalah bahwa hal itu dapat membuat objek lebih sulit digunakan dalam pengujian unit . Mockito tidak dapat membuat tiruan untuk metode statis dan Anda tidak bisa membuat implementasi subclass dari metode ini.

Jon Onstott
sumber
2
Anda tidak boleh menulis tes untuk metode pribadi. Lihat di sini . Tidak perlu menguji metode pribadi apakah itu statis atau tidak.
BW
@ BB Itu hal yang sangat subyektif. Ada banyak alasan yang valid untuk menguji metode pribadi.
ruohola
4

Jika metode ini pada dasarnya hanya sebuah subrutin yang tidak akan pernah dapat menggunakan informasi status sebelumnya, nyatakan itu statis.

Ini memungkinkannya untuk digunakan dalam metode statis lain atau inisialisasi kelas, yaitu:

public class Example {
   //...

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

   //...
}
Tidur
sumber
3

Pertanyaan statis / non-statis turun ke "apakah saya benar-benar perlu menggunakan objek dari kelas ini"?

Jadi, apakah Anda melewati objek di antara metode yang berbeda? Apakah objek berisi informasi yang berguna di luar konteks metode statis? Apakah ada alasan untuk tidak mendefinisikan metode kedua cara jika Anda akan menggunakan keduanya?

Jika Anda berada dalam dilema ini, menurut saya Anda memiliki semua data yang diperlukan untuk metode yang mengambang di kode Anda di luar objek. Apa ini yang kau inginkan? Apakah akan lebih mudah untuk selalu mengumpulkan data itu menjadi objek setiap kali? Anda mungkin ambivalen tentang komitmen terhadap model tunggal. Jika Anda dapat melakukan semuanya dengan satu cara, pilih yang statis atau non-statis dan ikuti saja.

tidak
sumber
3

Preferensi saya dalam kasus seperti ini adalah membuat computeOnedan computeMoremetode statis. Alasannya: enkapsulasi. Semakin sedikit kode yang memiliki akses ke implementasi kelas Anda, semakin baik.

Dalam contoh yang Anda berikan, Anda menyatakan bahwa computeOnedan computeMoretidak perlu mengakses bagian dalam kelas, jadi mengapa memberikan kesempatan bagi pengelola kelas untuk ikut campur dengan bagian dalam.

maxaposteriori
sumber
3

Saya ingin mengklarifikasi beberapa hal yang dikatakan poster lain sebagai informasi yang salah.

Pertama karena metode ini bersifat pribadi bahkan jika Anda menyatakannya statis, Anda tidak akan dapat mengaksesnya di luar kelas ini. Kedua mereka bersifat pribadi sehingga Anda bahkan tidak bisa menimpa dalam subkelas sehingga statis atau non-statis tidak ada bedanya. Ketiga, metode pribadi non-statis dapat dipanggil dari konstruktor kelas juga, itu tidak perlu statis.

Sekarang datang ke pertanyaan Anda jika metode pembantu pribadi harus didefinisikan sebagai statis atau non-statis. Saya akan pergi dengan jawaban Steve sebagai menandai metode privat statis menunjukkan bahwa metode ini stateless karena saya juga mengikuti aturan ini ketika saya kode.

Bhushan Bhangale
sumber
tetapi ada tempat-tempat di mana hanya metode statis yang bisa dipanggil, seperti dalam contoh saya dan dalam contoh Chris Marshall
Kip
Ya Kip jawaban Anda dan Chris sudah benar. Saya belum mengomentari mereka karena mereka adalah jawaban yang benar. Saya hanya berbicara tentang jawaban yang salah.
Bhushan Bhangale
3

Dari pengalaman saya akan menyatakan bahwa metode pribadi seperti itu cenderung sangat universal dan dapat digunakan kembali.

Saya pikir hal pertama yang harus dilakukan adalah mengajukan pertanyaan apakah metode ini mungkin berguna di luar konteks kelas saat ini. Jika demikian, saya akan melakukan apa yang Semua orang sarankan dan ekstrak metode ini sebagai statis ke beberapa kelas utilitas di mana seseorang mudah-mudahan memeriksa sebelum menerapkan metode baru melakukan hal yang persis sama.

Penggunaan metode pribadi yang umum seperti itu adalah sumber dari sebagian besar duplikasi kode dalam proyek karena setiap pengembang secara mandiri menemukan kembali mereka hanya di tempat yang ia perlukan untuk menggunakannya. Jadi sentralisasi metode semacam itu adalah cara untuk maju.

Piotr Sobczyk
sumber
2

Lebih khusus dengan contoh yang Anda berikan, tampaknya bahwa tujuan mendefinisikan metode ini lebih untuk kejelasan kode ketika Anda sedang membaca daripada untuk fungsi (mereka yang didefinisikan sebagai pribadi). Dalam hal itu, pergi dengan statis benar-benar tidak berguna bagi Anda, karena tujuan statis adalah untuk mengekspos fungsionalitas kelas.

tidak
sumber
Jawaban kecil ini, terkubur di bawah beban jawaban lain, benar-benar menyentuh inti masalah: fungsionalitas tingkat kelas vs fungsionalitas tingkat objek.
eljenso
Terima kasih, el, saya sangat menghargainya. Saya tidak keberatan suara sedikit up, baik :)
notnot
Saya memberi +1 pada saat saya menulis komentar.
eljenso
1
Saya tidak setuju. Kejelasan kode masih penting untuk metode pribadi. Ini mungkin kurang penting tetapi masih sesuatu yang harus diperjuangkan.
LegendLength
1

Salah satu alasannya adalah, semua yang dianggap sama, panggilan metode statis harus lebih cepat. Metode statis tidak boleh virtual, dan jangan mengambil referensi implisit ini.

dsimcha
sumber
Lihat jawaban saya di bawah ini (saya menambahkan analisis setelah itu sudah di -3, jadi tidak terlalu terlihat).
Michael Myers
1

Seperti yang dikatakan banyak orang, jadikan itu statis ! Berikut adalah aturan praktis yang saya ikuti: Jika Anda berpikir metode ini hanya fungsi matematika yaitu stateless, tidak melibatkan variabel instan (=> tidak ada vars warna biru [dalam gerhana] dalam metode), dan hasil dari metode akan sama untuk jumlah panggilan 'n' (dengan parameter yang sama, tentu saja), lalu tandai metode itu sebagai STATIC.

Dan jika Anda berpikir bahwa metode ini akan berguna untuk kelas lain maka pindahkan ke kelas Util jika tidak, letakkan metode ini sebagai privat di dalam sameclass. (meminimalkan aksesibilitas)

jai
sumber
1

Off-Topic: Saya akan menyimpan metode helper di kelas utilitas mandiri / helper dengan hanya metode statis di dalamnya.

Masalahnya dengan memiliki metode penolong pada titik penggunaan (baca 'kelas yang sama') adalah bahwa seseorang di telepon mungkin hanya memilih untuk memposting metode penolong mereka yang tidak terkait di tempat yang sama

Semua orang
sumber
-1: SO bukan forum pengiriman pesan. Anda akan melakukan yang lebih baik dengan benar-benar mengajukan pertanyaan yang tepat.
Stu Thompson
1
Saya lebih suka memiliki metode pembantu bersama dengan kelas mereka terikat / terkait dengan yang statis. Terlebih lagi, saya dapat mengekspos beberapa sebagai publik jika mereka harus digunakan secara eksternal. Dengan cara itu akan lebih mudah untuk mempertahankan API untuk kelas itu, jika seharusnya terbuka untuk umum dan digunakan secara intensif.
Ivaylo Slavov
1
class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

Keuntungan dari metode statis privat adalah bahwa mereka dapat digunakan kembali nanti jika Anda perlu menginisialisasi ulang variabel kelas.

mug896
sumber
1
  1. Tanpa pengubah statis, Anda tidak dapat mengetahui bahwa metode ini adalah stateless tanpa analisis tambahan yang dapat dengan mudah dilakukan ketika Anda (kembali) menulis metode tersebut.

  2. Kemudian pengubah "statis" dapat memberi Anda gagasan tentang refactoring selain hal-hal lain yang mungkin dianggap tidak berguna oleh orang lain. Misalnya memindahkan metode ke beberapa kelas Utilitas atau mengubahnya menjadi metode anggota ..

bodrin
sumber
0

Saya akan menyatakan mereka sebagai statis untuk menandai mereka sebagai stateless.

Java tidak memiliki mekanisme yang lebih baik untuk operasi kecil yang tidak diekspor, jadi saya pikir privat statis dapat diterima.

Uri
sumber