Mengapa kita tidak harus menggunakan statis yang dilindungi di java

122

Saya sedang melalui pertanyaan ini Apakah ada cara untuk mengganti variabel kelas di Jawa? Komentar pertama dengan 36 suara positif adalah:

Jika Anda pernah melihat protected static, jalankan.

Adakah yang bisa menjelaskan mengapa protected staticdisukai?

Zeeshan
sumber
6
Tidak ada yang salah dengan bidang statis yang dilindungi, selama itu final. Bidang statis yang bisa berubah yang dibagikan di seluruh kelas pasti menimbulkan kekhawatiran. Beberapa kelas yang memperbarui bidang statis sepertinya tidak dapat diandalkan atau mudah diikuti, terutama karena keberadaan bidang atau metode yang dilindungi menyiratkan bahwa kelas tersebut dimaksudkan untuk diperluas oleh kelas dalam paket lain, mungkin kelas yang tidak berada di bawah kendali penulis kelas yang berisi bidang yang dilindungi.
VGR
6
@VGR, finalbukan berarti bidang tidak dapat diubah. Anda selalu dapat memodifikasi yang objectdireferensikan oleh finalvariabel referensi.
Zeeshan
@VGR Saya tidak setuju. SATU-SATUNYA alasan Anda membuat variabel statis adalah untuk memiliki akses dari dalam paket lain hanya dengan pewarisan, dan akses ke satu bidang TIDAK boleh menjadi alasan untuk pewarisan. Ini adalah desain yang cacat, IMO, dan jika Anda menggunakan itu, Anda mungkin harus memikirkan kembali struktur aplikasi Anda. Itu hanya pendapat saya.
Dioksin
@LoneRider Anda benar. Saya berpikir tidak dapat diubah, dan final tentu saja tidak menjamin itu.
VGR
Bahkan saya datang ke sini dari pertanyaan yang sama.
Raj Rajeshwar Singh Rathore

Jawaban:

86

Ini lebih merupakan masalah gaya daripada masalah langsung. Ini menunjukkan bahwa Anda belum memikirkan dengan baik apa yang terjadi dengan kelas.

Pikirkan tentang apa staticartinya:

Variabel ini ada di tingkat kelas, tidak ada secara terpisah untuk setiap contoh dan tidak memiliki keberadaan independen di kelas yang memperpanjang saya .

Pikirkan tentang apa protectedartinya:

Variabel ini dapat dilihat oleh kelas ini, kelas dalam paket yang sama, dan kelas yang memperpanjang saya .

Kedua arti itu tidak sepenuhnya eksklusif tetapi cukup dekat.

Satu-satunya kasus yang dapat saya lihat di mana Anda dapat menggunakan keduanya bersama-sama adalah jika Anda memiliki kelas abstrak yang dirancang untuk diperpanjang dan kelas yang diperluas kemudian dapat mengubah perilaku menggunakan konstanta yang ditentukan dalam aslinya. Pengaturan semacam itu kemungkinan besar akan berakhir sangat berantakan dan menunjukkan kelemahan dalam desain kelas.

Dalam kebanyakan kasus, akan lebih baik untuk memiliki konstanta sebagai publik karena itu hanya membuat semuanya lebih bersih dan memungkinkan orang-orang untuk membuat sub-klasifikasi lebih fleksibel. Terlepas dari hal lain dalam banyak kasus, komposisi lebih disukai daripada pewarisan, sementara kelas abstrak memaksakan pewarisan.

Untuk melihat satu contoh bagaimana ini dapat merusak sesuatu dan untuk menggambarkan apa yang saya maksud dengan variabel yang tidak memiliki keberadaan independen coba kode contoh ini:

public class Program {
    public static void main (String[] args) throws java.lang.Exception {
        System.out.println(new Test2().getTest());
        Test.test = "changed";
        System.out.println(new Test2().getTest());
    }
}

abstract class Test {
    protected static String test = "test";
}

class Test2 extends Test {
    public String getTest() {
        return test;
    }
}

Anda akan melihat hasilnya:

test
changed

Coba sendiri di: https://ideone.com/KM8u8O

Kelas Test2dapat mengakses anggota statis testdari Testtanpa perlu mengkualifikasikan nama - tetapi tidak mewarisi atau mendapatkan salinannya sendiri. Ia melihat objek yang sama persis dalam memori.

Tim B
sumber
4
Kalian digantung pada warisan. Kasus khas di mana hal ini terlihat adalah seseorang menginginkan akses paket tanpa membuatnya menjadi publik (misalnya pengujian unit).
spudone
3
@spudone tetapi pengujian unit umumnya ditempatkan dalam paket yang sama. Untuk memberi mereka akses, Anda cukup menggunakan tingkat akses (paket) default. Protected memberikan akses ke subclass juga yang tidak diperlukan atau relevan dengan pengujian unit.
Tim B
1
@Kamafeather Protected berarti anak-anak dapat melihat Anda dari kelas yang berbeda dan kelas mana pun dalam paket yang sama dapat melihat Anda. Jadi ya Program ada dalam paket yang sama dan karenanya dapat melihat anggota yang dilindungi.
Tim B
2
" kelas perluasan kemudian dapat mengubah perilaku " Ini akan melanggar prinsip Subsitusi Liskov. Subkelas tidak boleh mengubah perilaku supertipe itu. Ini termasuk dalam keseluruhan argumen "a square is not a rectangle": menyesuaikan superclass ( Rectangle) untuk menyesuaikan lebar / tinggi untuk memastikan kesetaraan akan (untuk Square) akan menghasilkan hasil yang tidak diinginkan jika Anda mengganti setiap supertipe dengan instance dari subtipe itu.
Dioksin
1
" Satu-satunya kasus yang dapat saya lihat di mana Anda dapat menggunakan keduanya bersama-sama adalah jika Anda memiliki kelas abstrak yang dirancang untuk diperpanjang dan kelas yang diperluas kemudian dapat mengubah perilaku menggunakan konstanta yang ditentukan dalam aslinya " Sedikit tersembunyi, tetapi ada di sana. Contoh kode juga mengungkapkan pernyataan
Dioxin
32

Ini disukai karena kontradiktif.

Membuat variabel protected menyiratkan itu akan digunakan dalam paket atau akan diwarisi dalam subkelas .

Membuat variabel staticmenjadikannya anggota kelas, menghilangkan niat untuk mewarisinya . Ini hanya menyisakan niat untuk digunakan dalam sebuah paket , dan kami memiliki package-privateuntuk itu (tanpa pengubah).

Satu-satunya situasi yang saya anggap berguna ini adalah jika Anda mendeklarasikan kelas yang harus digunakan untuk meluncurkan aplikasi (seperti JavaFX's Application#launch , dan hanya ingin dapat meluncurkan dari subkelas. Jika melakukannya, pastikan metodenya juga finaluntuk disallow menyembunyikan . Tapi ini bukan "norma", dan mungkin diterapkan untuk mencegah penambahan lebih banyak kerumitan dengan menambahkan cara baru untuk meluncurkan aplikasi.

Untuk melihat tingkat akses setiap pengubah, lihat ini: Tutorial Java - Mengontrol Akses ke Anggota Kelas

Dioksin
sumber
4
Saya tidak mengerti bagaimana staticmenghilangkan niat mewarisinya. Karena subclass saya di paket lain masih membutuhkan field super untuk bisa protectedmengaksesnya, padahal itu static. package-privatetidak dapat membantu
Aobo Yang
@AoboYang Anda benar, itulah sebabnya beberapa orang menggunakan protected static. Tapi itu bau kode, karena itu bagian " lari ". Pengubah akses dan pewarisan adalah dua subjek yang berbeda. Ya, Anda tidak akan dapat mengakses anggota statis dari kelas super jika itu package-private. Namun Anda tidak boleh mengandalkan warisan ke staticbidang referensi sejak awal; itu pertanda desain yang buruk. Anda akan melihat upaya mengganti staticmetode tidak memberikan hasil, yang merupakan tanda yang jelas bahwa warisan tidak berbasis kelas. Jika Anda memerlukan akses di luar kelas atau paket, itu haruspublic
Dioxin
3
Saya memiliki kelas dengan beberapa private staticfungsi util pada awalnya, tetapi saya pikir seseorang mungkin ingin meningkatkan atau menyesuaikan dari kelas saya dan fungsi util ini juga dapat memberikan kemudahan bagi mereka. publicmungkin tidak sesuai karena metode util bukan untuk pengguna instance kelas saya. Bisakah Anda membantu saya menemukan desain yang bagus protected? Terima kasih
Aobo Yang
Di tautan itu, tertulis 'Gunakan tingkat akses paling ketat yang masuk akal untuk anggota tertentu. Gunakan pribadi kecuali Anda punya alasan kuat untuk tidak melakukannya. ' , yang bertentangan dengan pertanyaan ini, ada pemikiran?
Cecilia
13

Saya tidak melihat alasan khusus mengapa hal ini tidak disukai. Mungkin selalu ada alternatif untuk mencapai perilaku yang sama, dan itu akan tergantung pada arsitektur sebenarnya apakah alternatif ini "lebih baik" daripada metode statis yang dilindungi atau tidak. Tetapi satu contoh di mana metode statis yang dilindungi akan masuk akal, setidaknya, adalah sebagai berikut:

(Diedit untuk dipecah menjadi paket terpisah, agar penggunaan protectedlebih jelas)

package a;
import java.util.List;

public abstract class BaseClass
{
    public Integer compute(List<Integer> list)
    {
        return computeDefaultA(list)+computeDefaultB(list);
    }

    protected static Integer computeDefaultA(List<Integer> list)
    {
        return 12;
    }
    protected static Integer computeDefaultB(List<Integer> list)
    {
        return 34;
    }
}

Berasal dari itu:

package a.b;

import java.util.List;

import a.BaseClass;

abstract class ExtendingClassA extends BaseClass
{
    @Override
    public Integer compute(List<Integer> list)
    {
        return computeDefaultA(list)+computeOwnB(list);
    }

    private static Integer computeOwnB(List<Integer> list)
    {
        return 56;
    }
}

Kelas turunan lainnya:

package a.b;

import java.util.List;

import a.BaseClass;

abstract class ExtendingClassB extends BaseClass
{
    @Override
    public Integer compute(List<Integer> list)
    {
        return computeOwnA(list)+computeDefaultB(list);
    }

    private static Integer computeOwnA(List<Integer> list)
    {
        return 78;
    }
}

The protected staticpengubah tentu dapat dibenarkan di sini:

  • Metodenya bisa static, karena tidak bergantung pada variabel instan. Mereka tidak dimaksudkan untuk digunakan secara langsung sebagai metode polimorfik, melainkan metode "utilitas" yang menawarkan implementasi default yang merupakan bagian dari komputasi yang lebih kompleks, dan berfungsi sebagai "blok bangunan" dari implementasi yang sebenarnya.
  • Metode tidak boleh public, karena merupakan detail implementasi. Dan mereka tidak bisa privatekarena mereka harus dipanggil oleh kelas tambahan. Mereka juga tidak bisa memiliki visibilitas "default", karena mereka tidak akan bisa diakses oleh kelas-kelas yang diperluas di paket lain.

(EDIT: Orang dapat berasumsi bahwa komentar asli hanya mengacu pada bidang , dan bukan metode - maka, bagaimanapun, itu terlalu umum)

Marco13
sumber
Dalam kasus ini, Anda harus mendefinisikan implementasi default sebagai protected final(karena Anda tidak ingin mereka diganti), bukan static. Fakta bahwa suatu metode tidak menggunakan variabel instan tidak berarti itu harus static(meskipun bisa ).
barfuin
2
@ Thomas Tentu, dalam hal ini, ini juga mungkin. Secara umum: Ini tentu saja sebagian subjektif, tetapi aturan praktis saya adalah: Jika suatu metode tidak dimaksudkan untuk digunakan secara polimorfis, dan dapat menjadi statis, maka saya membuatnya statis. Berbeda dengan membuatnya final, ini tidak hanya menjelaskan bahwa metode tersebut tidak dimaksudkan untuk diganti, tetapi juga menjelaskan kepada pembaca bahwa metode tersebut tidak menggunakan variabel instan. Singkatnya: Tidak ada alasan untuk tidak membuatnya statis.
Marco13
1
Jika suatu metode tidak dimaksudkan untuk digunakan secara polimorfis, dan dapat menjadi statis, maka saya membuatnya menjadi statis. - Ini akan membuat Anda mendapat masalah saat Anda mulai menggunakan kerangka kerja tiruan untuk pengujian unit. Tapi ini membawa kita ke topik yang berbeda ...
barfuin
@ Marco13 juga ada kasus ketika Anda membuat sesuatu protected, bukan untuk menunjukkan warisan, tetapi menyimpannya di bawah satu paket - tidak diekspos. Saya kira antarmuka tersegel akan membantu dengan cara ini ketika mereka melakukannya di java
Eugene
@Eugene Komentar itu sedikit mengganggu saya. Visibilitas paket dapat dicapai tanpa pengubah apa pun, sedangkanprotected berarti "Mewarisi kelas (bahkan dalam paket yang berbeda)" ...
Marco13
7

Anggota statis tidak diwariskan, dan anggota yang dilindungi hanya dapat dilihat oleh subkelas (dan tentu saja kelas yang memuatnya), jadi a protected staticmemiliki visibilitas yang sama dengan static, menunjukkan kesalahpahaman oleh pembuat kode.

Bohemian
sumber
1
Bisakah Anda memberikan sumber untuk pernyataan pertama Anda? Dalam pengujian cepat, int statis yang dilindungi diwarisi dan digunakan kembali dalam subkelas tanpa masalah.
hiergiltdiestfu
Statis yang dilindungi memiliki visibilitas yang sama dengan statik pribadi paket, bukan statik pribadi. Untuk menambahkan ini, jika Anda menggunakan dilindungi dan statis, yang terbaik adalah hanya menghapus pengubah akses untuk membuatnya menjadi paket-pribadi (jika Anda bermaksud untuk membuatnya dapat diakses dalam paket)
Dioxin
2
Uhm ... tidak. paket pribadi dan protectedtidak sama. Jika Anda hanya mengatakan static, bidang ini hanya dapat dilihat oleh subclass dalam paket yang sama.
Aaron Digulla
1
@AaronDigulla protected staticmemungkinkan akses di dalam paket atau dari subclass . Membuat variabel statis menghapus maksud subclassing, meninggalkan satu-satunya maksud adalah akses dari dalam paket.
Dioksin
@VinceEmigh: Maaf, saya sedang berbicara dengan Bohemian. Seharusnya membuat ini lebih jelas.
Aaron Digulla
5

Sebenarnya tidak ada yang salah secara fundamental protected static. Jika Anda benar-benar menginginkan variabel atau metode statis yang terlihat untuk paket dan semua subclass dari kelas yang mendeklarasikan, lanjutkan dan buatprotected static .

Beberapa orang umumnya menghindari penggunaan protectedkarena berbagai alasan dan beberapa orang berpikir staticvariabel non-final harus dihindari dengan segala cara (saya pribadi bersimpati dengan yang terakhir sampai taraf tertentu), jadi saya kira kombinasi protecteddan staticharus terlihat buruk ^ 2 untuk yang milik kedua kelompok.

x4u
sumber
3

Protected digunakan sehingga bisa digunakan di subclass. Tidak ada logika dalam mendefinisikan statis yang dilindungi saat menggunakan dalam konteks kelas konkret karena Anda dapat mengakses variabel yang sama dengan cara statis. Namun pemohon akan memberikan peringatan untuk mengakses variabel statis kelas super dengan cara statis.

Mithun Kannoth
sumber
1

Nah, seperti yang sebagian besar orang jawab:

  • protectedberarti - ' paket-privat + visibilitas ke subkelas - properti / perilaku DIBERIKAN '
  • staticberarti - ' kebalikan dari instance - ini adalah properti / perilaku CLASS, yaitu TIDAK DIWARISKAN '

Oleh karena itu, mereka sedikit kontradiktif dan tidak cocok.

Namun, baru-baru ini saya menemukan kasus penggunaan di mana mungkin masuk akal untuk menggunakan keduanya secara bersamaan. Bayangkan Anda ingin membuat abstractkelas yang merupakan induk untuk tipe yang tidak dapat diubah dan memiliki sekumpulan properti yang sama untuk subtipe. Untuk mengimplementasikan keabadian dengan benar dan menjaga keterbacaan, seseorang mungkin memutuskan untuk menggunakan pola Builder .

package X;
public abstract class AbstractType {
    protected Object field1;
    protected Object field2;
    ...
    protected Object fieldN;

    protected static abstract class BaseBuilder<T extends BaseBuilder<T>> {
        private Object field1; // = some default value here
        private Object field2; // = some default value here
        ...
        private Object fieldN; // = some default value here

        public T field1(Object value) { this.field1 = value; return self();}
        public T field2(Object value) { this.field2 = value; return self();}
        ...
        public T fieldN(Object value) { this.fieldN = value; return self();}
        protected abstract T self(); // should always return this;
        public abstract AbstractType build();
    }

    private AbstractType(BaseBuilder<?> b) {
        this.field1 = b.field1;
        this.field2 = b.field2;
        ...
        this.fieldN = b.fieldN;
    }
}

Dan kenapa protected static? Karena saya ingin subtipe non-abstrak AbstactTypeyang mengimplementasikan Builder non-abstraknya sendiri dan terletak di luar package Xagar dapat mengakses dan menggunakan kembali BaseBuilder.

package Y;
public MyType1 extends AbstractType {
    private Object filedN1;

    public static class Builder extends AbstractType.BaseBuilder<Builder> {
        private Object fieldN1; // = some default value here

        public Builder fieldN1(Object value) { this.fieldN1 = value; return self();}
        @Override protected Builder self() { return this; }
        @Override public MyType build() { return new MyType(this); }
    }

    private MyType(Builder b) {
        super(b);
        this.fieldN1 = b.fieldN1;
    }
}

Tentu saja kita dapat BaseBuildermempublikasikannya tetapi kemudian kita sampai pada pernyataan kontradiktif lainnya:

  • Kami memiliki kelas non-instantiatable (abstrak)
  • Kami menyediakan pembangun publik untuk itu

Jadi dalam kedua kasus dengan protected staticdan publicpembangun fileabstract class kami menggabungkan pernyataan yang kontradiktif. Ini adalah masalah preferensi pribadi.

Namun, saya masih lebih memilih publicbuilderabstract class karena protected staticbagi saya terasa lebih tidak wajar di dunia OOD dan OOP!

egelev
sumber
0

Tidak ada salahnya memiliki protected static. Satu hal yang diabaikan oleh banyak orang adalah Anda mungkin ingin menulis kasus uji untuk metode statis yang tidak ingin Anda ungkapkan dalam keadaan normal. Saya perhatikan ini sangat berguna untuk menulis tes untuk metode statis di kelas utilitas.

Aelphaeis
sumber