Apakah ada cara untuk mengganti variabel kelas di Java?

161
class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";
}

public void doIt()
{
    new Son().printMe();
}

Fungsi doIt akan mencetak "ayah". Apakah ada cara untuk membuatnya mencetak "anak"?

Dikla
sumber
111
Jika Anda pernah melihat statis yang dilindungi, jalankan.
Tom Hawtin - tackline
23
@ TomHawtin-tackline memaafkan ketidaktahuan saya, tetapi mengapa statis dilindungi disukai? Saya mencoba googling tetapi tidak dapat menemukan jawaban yang jelas. Terima kasih
Tony Chan

Jawaban:

68

Iya. Tetapi karena variabel yang bersangkutan itu ditimpa (Memberikan nilai baru ke variabel. Memberikan definisi baru untuk fungsi adalah Override).Just don't declare the variable but initialize (change) in the constructor or static block.

Nilai akan tercermin ketika menggunakan di blok kelas induk

jika variabel statis maka ubah nilai selama inisialisasi itu sendiri dengan blok statis,

class Son extends Dad {
    static { 
       me = 'son'; 
    }
}

atau perubahan konstruktor.

Anda juga dapat mengubah nilai nanti di blok mana pun. Itu akan tercermin di kelas super

Vivek MVK
sumber
10
Saya pikir jawabannya harus memiliki beberapa kode agar lebih mudah dibaca. Ubah saja implementasi Son menjadi class Son extends Dad { static { me = 'son' } }
Cléssio Mendes
97

Singkatnya, tidak, tidak ada cara untuk mengganti variabel kelas.

Anda tidak menimpa variabel kelas di Jawa yang Anda sembunyikan. Overriding misalnya adalah metode. Menyembunyikan berbeda dari mengganti.

Dalam contoh yang Anda berikan, dengan mendeklarasikan variabel kelas dengan nama 'saya' di kelas Son, Anda menyembunyikan variabel kelas yang akan diwarisi dari ayah superclassnya dengan nama yang sama 'saya'. Menyembunyikan variabel dengan cara ini tidak mempengaruhi nilai variabel kelas 'saya' di superclass Dad.

Untuk bagian kedua dari pertanyaan Anda, bagaimana membuatnya mencetak "nak", saya akan menetapkan nilainya melalui konstruktor. Meskipun kode di bawah ini sangat berbeda dengan pertanyaan awal Anda, saya akan menulisnya seperti ini;

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void printName() {
        System.out.println(name);
    }
}

JLS memberikan lebih banyak detail tentang persembunyian di bagian 8.3 - Deklarasi Lapangan

Mike
sumber
1
Override dimungkinkan dengan memiliki blok statis di kelas turunan
siddagrl
1
@siddagrl: dapatkah Anda menjelaskan?
Mr_and_Mrs_D
kelas OP mana yang Personseharusnya diganti dalam contoh ini?
n611x007
1
@naxa Sondan Dadseharusnya mewarisi dari Person, lalu memanggil super("Son or Dad");konstruktor mereka.
Panzercrisis
Apakah dianggap baik atau buruk untuk menyembunyikan variabel seperti itu di Jawa?
Panzercrisis
31

Ya, cukup timpa printMe()metode ini:

class Son extends Dad {
        public static final String me = "son";

        @Override
        public void printMe() {
                System.out.println(me);
        }
}
Romain Linsolas
sumber
bagaimana jika perpustakaan yang Anda gunakan tidak memiliki printMe()metode, atau bahkan memiliki metode itu, itu adalah metode statis?
Venkat Sudheer Reddy Aedama
1
Untuk menjadikan ini contoh kehidupan nyata, Anda bisa mempertimbangkan bahwa metode "printMe" bisa menjadi fungsi yang sangat rumit dengan logika yang tidak ingin Anda ulangi. Dalam contoh ini, Anda hanya ingin mengubah nama orang tersebut, bukan logika yang mencetaknya. Anda harus membuat metode yang disebut "getName" yang akan Anda timpa untuk mengembalikan "son" dan metode printMe akan memanggil metode getName sebagai bagian dari pencetakannya.
Ring
17

Anda dapat membuat pengambil dan menimpa pengambil itu. Ini sangat berguna jika variabel yang Anda override adalah sub-kelas dari dirinya sendiri. Bayangkan kelas super Anda memiliki Objectanggota tetapi di sub-kelas Anda ini sekarang lebih didefinisikan sebagai Integer.

class Dad
{
        private static final String me = "dad";

        protected String getMe() {
            return me;
        }

        public void printMe()
        {
                System.out.println(getMe());
        }
}

class Son extends Dad
{
        private static final String me = "son";

        @Override
        protected String getMe() {
            return me;
        }
}

public void doIt()
{
        new Son().printMe(); //Prints "son"
}
John Strickler
sumber
11

Jika Anda akan menimpanya, saya tidak melihat alasan yang valid untuk menjaga ini statis. Saya akan menyarankan penggunaan abstraksi (lihat kode contoh). :

     public interface Person {
        public abstract String getName();
       //this will be different for each person, so no need to make it concrete
        public abstract void setName(String name);
    }

Sekarang kita bisa menambahkan Dad:

public class Dad implements Person {

    private String name;

    public Dad(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
    return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

anak laki-laki:

public class Son implements Person {

    private String name;

    public Son(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
        return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

dan Ayah bertemu seorang wanita baik:

public class StepMom implements Person {

    private String name;

    public StepMom(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
        return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

Sepertinya kita memiliki keluarga, mari beri tahu dunia nama mereka:

public class ConsoleGUI {

    public static void main(String[] args) {
        List<Person> family = new ArrayList<Person>();
        family.add(new Son("Tommy"));
        family.add(new StepMom("Nancy"));
        family.add(new Dad("Dad"));
        for (Person person : family) {
            //using the getName vs printName lets the caller, in this case the
            //ConsoleGUI determine versus being forced to output through the console. 
            System.out.print(person.getName() + " ");
            System.err.print(person.getName() + " ");
            JOptionPane.showMessageDialog(null, person.getName());
    }
}

}

Output System.out: Tommy Nancy Dad
System.err sama seperti di atas (hanya memiliki font merah)
Output JOption:
Tommy lalu
Nancy lalu
Dad

nckbrz
sumber
1
OP tentang perubahan akses ke statika. Oleh karena itu "aku" adalah "ayah" atau "putra" generik - sesuatu yang tidak perlu dibuat untuk setiap ayah atau anak dan karenanya adalah statis.
RichieHH
Ya, benar, saya tidak melihat itu statis. Haha, itu akan mengubah jawaban saya menjadi kurang lebih 100 baris kode, jika saya jawab sama sekali. Terima kasih atas perhatiannya
nckbrz
6

Ini terlihat seperti cacat desain.

Hapus kata kunci statis dan atur variabel misalnya dalam konstruktor. Dengan cara ini Son hanya menetapkan variabel ke nilai yang berbeda di konstruktornya.

Patrick Cornelissen
sumber
1
apa yang salah tentang ini? Jika variabel anggota 'saya' seharusnya dapat ditandingi dalam desain Anda, maka patrick adalah solusi yang tepat
Chii
Sebenarnya, jika nilai saya sama untuk setiap contoh, hanya menghapus 'statis' akan baik-baik saja. Inisialisasi tidak harus dalam konstruktor.
Nate Parsons
Benar, meskipun secara teknis (di bytecode) saya pikir itu hampir sama ;-)
Patrick Cornelissen
4

Meskipun benar bahwa variabel kelas hanya dapat disembunyikan di subclass, dan tidak diganti, masih mungkin untuk melakukan apa yang Anda inginkan tanpa menimpa printMe ()subclass, dan refleksi adalah teman Anda. Dalam kode di bawah ini saya menghilangkan penanganan pengecualian untuk kejelasan. Harap dicatat bahwa menyatakan mesebagai protectedtampaknya tidak memiliki banyak akal dalam konteks ini, seperti yang akan disembunyikan di subclass ...

class Dad
  {
    static String me = "dad";

    public void printMe ()
      {
        java.lang.reflect.Field field = this.getClass ().getDeclaredField ("me");
        System.out.println (field.get (null));
      }
  }
Sergey Ushakov
sumber
Sangat menarik, Java.lang.reflect ya? ... +1
nckbrz
3
class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String _me = me = "son";
}

public void doIt()
{
    new Son().printMe();
}

... akan mencetak "nak".

pengguna2895864
sumber
Bukankah ini sama dengan pertanyaan awal?
Corley Brigman
Bisakah Anda jelaskan mengapa? (dalam jawaban Anda)
Mr_and_Mrs_D
3

https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html

Ini disebut Menyembunyikan Bidang

Dari tautan di atas

Di dalam kelas, bidang yang memiliki nama yang sama dengan bidang dalam superclass menyembunyikan bidang superclass, bahkan jika jenisnya berbeda. Di dalam subclass, bidang dalam superclass tidak dapat dirujuk dengan nama sederhana. Sebagai gantinya, bidang harus diakses melalui super, yang dibahas di bagian selanjutnya. Secara umum, kami tidak menyarankan menyembunyikan bidang karena membuat kode sulit dibaca.

sadjkas sadal
sumber
1
Tautan ke solusi potensial selalu diterima, tetapi harap tambahkan konteks di sekitar tautan sehingga sesama pengguna Anda akan mengetahui apa itu dan mengapa ada di sana. Selalu kutip bagian yang paling relevan dari tautan penting, jika situs target tidak dapat dijangkau atau offline secara permanen. Mempertimbangkan bahwa menjadi lebih dari sekadar tautan ke situs eksternal adalah kemungkinan alasan mengapa dan bagaimana beberapa jawaban dihapus? .
FelixSFD
2

hanya dengan mengganti printMe():

class Son extends Dad 
{
    public void printMe() 
    {
        System.out.println("son");
    }
}

referensi medalam Dad.printMemetode ini secara implisit menunjuk ke bidang statis Dad.me, jadi dengan satu atau lain cara Anda mengubah apa yang printMedilakukan di Son...

Dan Vinton
sumber
2

Anda tidak bisa menimpa variabel di kelas. Anda hanya dapat mengganti metode. Anda harus merahasiakan variabelnya jika tidak, Anda bisa mendapatkan banyak masalah.

Ionel Bratianu
sumber
Anda tidak dapat menimpa statika apa pun.
Tom Hawtin - tackline
(atau setidaknya itu tidak masuk akal, IFSWIM.)
Tom Hawtin - tackline
Benar, Anda tidak bisa mengesampingkan statika. Anda dapat SHADOW mereka atau beberapa orang memanggil saya MASKING.
Dale
2

Ini memang mencetak 'ayah', karena bidangnya tidak diganti tetapi disembunyikan. Ada tiga pendekatan untuk membuatnya mencetak 'anak':

Pendekatan 1: ganti printMe

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";

    @override
    public void printMe()
    {
        System.out.println(me);
    }
}

public void doIt()
{
    new Son().printMe();
}

Pendekatan 2: jangan sembunyikan bidang dan inisialisasi di konstruktor

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    public Son()
    {
        me = "son";
    }
}

public void doIt()
{
    new Son().printMe();
}

Pendekatan 3: gunakan nilai statis untuk menginisialisasi bidang dalam konstruktor

class Dad
{
    private static String meInit = "Dad";

    protected String me;

    public Dad() 
    {
       me = meInit;
    }

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    private static String meInit = "son";

    public Son()
    {
        me = meInit;
    }

}

public void doIt()
{
    new Son().printMe();
}
Rik Schaaf
sumber
2

Variabel tidak ikut serta dalam overrinding. Hanya metode yang melakukannya. Panggilan metode diselesaikan pada saat runtime, yaitu, keputusan untuk memanggil metode diambil pada saat runtime, tetapi variabel diputuskan pada waktu kompilasi saja. Oleh karena itu variabel dipanggil yang referensi digunakan untuk memanggil dan bukan dari objek runtime.

Lihatlah cuplikan berikut:

package com.demo;

class Bike {
  int max_speed = 90;
  public void disp_speed() {
    System.out.println("Inside bike");
 }
}

public class Honda_bikes extends Bike {
  int max_speed = 150;
  public void disp_speed() {
    System.out.println("Inside Honda");
}

public static void main(String[] args) {
    Honda_bikes obj1 = new Honda_bikes();
    Bike obj2 = new Honda_bikes();
    Bike obj3 = new Bike();

    obj1.disp_speed();
    obj2.disp_speed();
    obj3.disp_speed();

    System.out.println("Max_Speed = " + obj1.max_speed);
    System.out.println("Max_Speed = " + obj2.max_speed);
    System.out.println("Max_Speed = " + obj3.max_speed);
  }

}

Saat Anda menjalankan kode, konsol akan menunjukkan:

Inside Honda
Inside Honda
Inside bike

Max_Speed = 150
Max_Speed = 90
Max_Speed = 90
Ananta SE
sumber
0

Tentu saja menggunakan atribut pribadi, dan getter dan setter akan menjadi hal yang disarankan untuk dilakukan, tetapi saya menguji yang berikut, dan berfungsi ... Lihat komentar dalam kode

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";

    /* 
    Adding Method printMe() to this class, outputs son 
    even though Attribute me from class Dad can apparently not be overridden
    */

    public void printMe()
    {
        System.out.println(me);
    }
}

class Tester
{
    public static void main(String[] arg)
    {
        new Son().printMe();
    }
}

Sooo ... apakah saya baru saja mendefinisikan kembali aturan warisan atau apakah saya menempatkan Oracle ke dalam situasi yang rumit? Bagi saya, static String me yang terproteksi jelas diganti, seperti yang Anda lihat ketika Anda menjalankan program ini. Juga, tidak masuk akal bagi saya mengapa atribut tidak boleh ditimpa.

Pixel
sumber
1
Dalam hal ini Anda "menyembunyikan" variabel dari kelas super. Ini berbeda dari mengesampingkan dan tidak ada hubungannya dengan warisan. Kelas-kelas lain akan melihat satu variabel atau yang lain tergantung pada apakah mereka mereferensikan kelas-dasar atau sub-kelas dan yang mereka lihat diperbaiki pada waktu kompilasi. Jika Anda mengubah nama "saya" di sub kelas menjadi sesuatu yang lain, Anda akan mendapatkan efek yang sama. Sebagian besar IDE dan alat validasi kode (findbugs dll) akan memperingatkan Anda ketika Anda menyembunyikan variabel seperti ini karena biasanya bukan itu yang ingin Anda lakukan.
AutomatedMike
Anda melihat pemrograman dari perspektif pengkodean saja. Yang bagus, kode apa pun yang Anda inginkan dalam kasus itu. Jika Anda melihat pengkodean dari tampilan anggota tim, maka aturan menjadi jelas, seperti mengapa bidang tidak dapat ditimpa dan sebagainya. Saya bisa memberi Anda pidato tentang alasannya, tetapi jika Anda tidak melihatnya dari sudut pandang seorang anggota tim, Anda hanya akan melihat poin saya tidak valid dan melemparkan argumen yang tidak valid kepada saya.
nckbrz
0

Mengapa Anda ingin menimpa variabel ketika Anda dapat dengan mudah menetapkan kembali mereka di subClasses.

Saya mengikuti pola ini untuk mengerjakan desain bahasa. Asumsikan sebuah kasus di mana Anda memiliki kelas layanan yang berat dalam kerangka kerja Anda yang perlu digunakan dalam rasa yang berbeda di beberapa aplikasi turunan. Dalam kasus itu, cara terbaik untuk mengkonfigurasi logika kelas super adalah dengan menetapkan kembali variabel 'mendefinisikan' nya.

public interface ExtensibleService{
void init();
}

public class WeightyLogicService implements ExtensibleService{
    private String directoryPath="c:\hello";

    public void doLogic(){
         //never forget to call init() before invocation or build safeguards
         init();
       //some logic goes here
   }

   public void init(){}    

}

public class WeightyLogicService_myAdaptation extends WeightyLogicService {
   @Override
   public void init(){
    directoryPath="c:\my_hello";
   }

}
premganz
sumber
0

Tidak. Variabel kelas (Juga berlaku untuk variabel instan) tidak menunjukkan fitur utama dalam Java karena variabel kelas dipanggil berdasarkan jenis objek panggilan. Menambahkan satu kelas lagi (Manusia) dalam hierarki untuk membuatnya lebih jelas. Jadi sekarang sudah

Putra meluas Ayah melebar Manusia

Dalam kode di bawah ini, kami mencoba untuk beralih di atas array objek Manusia, Ayah dan Anak, tetapi mencetak nilai Kelas Manusia dalam semua kasus karena jenis objek panggilan adalah Manusia.

    class Human
{
    static String me = "human";

    public void printMe()
    {
        System.out.println(me);
    }
}
class Dad extends Human
{
    static String me = "dad";

}

class Son extends Dad
{
    static String me = "son";
}


public class ClassVariables {
    public static void main(String[] abc)   {
        Human[] humans = new Human[3];
        humans[0] = new Human();
        humans[1] = new Dad();
        humans[2] = new Son();
        for(Human human: humans)   {
            System.out.println(human.me);        // prints human for all objects
        }
    }
}

Akan dicetak

  • manusia
  • manusia
  • manusia

Jadi tidak ada variabel Kelas utama.

Jika kita ingin mengakses variabel kelas dari objek aktual dari variabel referensi dari kelas induknya, kita perlu secara eksplisit mengatakan ini ke kompiler dengan casting referensi induk (objek Manusia) ke tipenya.

    System.out.println(((Dad)humans[1]).me);        // prints dad

    System.out.println(((Son)humans[2]).me);        // prints son

Akan dicetak

  • ayah
  • putra

Pada bagian bagaimana pertanyaan ini: - Seperti yang sudah disarankan menimpa metode printMe () di kelas Son, kemudian pada panggilan

Son().printMe();

Variabel Kelas Ayah "aku" akan disembunyikan karena deklarasi terdekat (dari metode printme Kelas Anak) dari "aku" (di kelas Son) akan mendapatkan prioritas.

Pengembang
sumber
0

Panggil saja super.variable di konstruktor sub kelas

public abstract class Beverage {

int cost;


int getCost() {

    return cost;

}

}`

public class Coffee extends Beverage {


int cost = 10;
Coffee(){
    super.cost = cost;
}


}`

public class Driver {

public static void main(String[] args) {

    Beverage coffee = new Coffee();

    System.out.println(coffee.getCost());

}

}

Outputnya adalah 10.

fractal80y
sumber