Có cách nào để ghi đè các biến lớp trong Java không?


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

Hàm doIt sẽ in "cha". Có cách nào để làm cho nó in "con trai"?


111
Nếu bạn từng thấy một tĩnh được bảo vệ, hãy chạy.
Tom Hawtin - tackline

23
@ TomHawtin-tackline tha thứ cho sự thiếu hiểu biết của tôi, nhưng tại sao một tĩnh được bảo vệ lại nhăn mặt? Tôi đã cố gắng googling nhưng không thể tìm thấy một câu trả lời rõ ràng. Cảm ơn
Tony Chan

Câu trả lời:


68

Đúng. Nhưng khi biến có liên quan, nó được ghi đè (Đưa giá trị mới cho biến. Đưa ra định nghĩa mới cho hàm là Ghi đè).Just don't declare the variable but initialize (change) in the constructor or static block.

Giá trị sẽ được phản ánh khi sử dụng trong các khối của lớp cha

nếu biến là tĩnh thì thay đổi giá trị trong quá trình khởi tạo chính nó bằng khối tĩnh,

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

hoặc thay đổi khác trong constructor.

Bạn cũng có thể thay đổi giá trị sau này trong bất kỳ khối nào. Nó sẽ được phản ánh trong siêu hạng


10
Tôi nghĩ rằng câu trả lời nên có một số mã để dễ đọc hơn. Chỉ cần thay đổi triển khai Son thành class Son extends Dad { static { me = 'son' } }
Cléssio Mendes

97

Nói tóm lại, không, không có cách nào để ghi đè một biến lớp.

Bạn không ghi đè các biến lớp trong Java mà bạn ẩn chúng. Ghi đè là phương pháp ví dụ. Ẩn khác với ghi đè.

Trong ví dụ bạn đã đưa ra, bằng cách khai báo biến lớp có tên 'tôi' trong lớp Son, bạn ẩn biến lớp mà nó sẽ được thừa hưởng từ cha siêu lớp của nó có cùng tên 'tôi'. Ẩn một biến theo cách này không ảnh hưởng đến giá trị của biến lớp 'tôi' trong lớp cha siêu lớp.

Đối với phần thứ hai của câu hỏi của bạn, về cách làm cho nó in "con trai", tôi sẽ đặt giá trị thông qua hàm tạo. Mặc dù đoạn mã dưới đây rời khỏi câu hỏi ban đầu của bạn khá nhiều, tôi sẽ viết nó giống như thế này;

public class Person {
    private String name;

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

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

JLS cung cấp nhiều chi tiết hơn về việc ẩn trong phần 8.3 - Tuyên bố thực địa


1
Có thể ghi đè bằng cách có một khối tĩnh trong lớp dẫn xuất
siddagrl

1
@siddagrl: bạn có thể giải thích?
Mr_and_Mrs_D

lớp nào của OP được Personcho là sẽ thay thế trong ví dụ này?
n611x007

1
@naxa SonDadđược cho là thừa kế từ Person, sau đó gọi super("Son or Dad");các nhà xây dựng của họ.
Panzercrisis

Nó được coi là tốt hay xấu để ẩn các biến như thế trong Java?
Panzercrisis

31

Có, chỉ cần ghi đè printMe()phương thức:

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

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

Điều gì xảy ra nếu thư viện mà bạn đang sử dụng không có printMe()phương thức hoặc thậm chí nó có phương thức đó là phương thức tĩnh?
Venkat Sudheer Reddy Aedama

1
Để biến điều này thành một ví dụ thực tế hơn, bạn có thể xem xét rằng phương thức "printMe" có thể là một hàm rất phức tạp với logic mà bạn không muốn lặp lại. Trong ví dụ này, bạn chỉ muốn thay đổi tên của người đó, chứ không phải logic in nó. Bạn nên tạo một phương thức gọi là "getName" mà bạn sẽ ghi đè để trả về "son" và phương thức printMe sẽ gọi phương thức getName như một phần của việc in.
Đổ chuông

17

Bạn có thể tạo một getter và sau đó ghi đè lên getter đó. Nó đặc biệt hữu ích nếu biến bạn ghi đè là một lớp con của chính nó. Hãy tưởng tượng siêu hạng của bạn có một Objectthành viên nhưng trong lớp phụ của bạn, giờ đây nó được xác định rõ hơn là một 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"
}

11

Nếu bạn định ghi đè lên thì tôi không thấy lý do hợp lệ để giữ trạng thái tĩnh này. Tôi sẽ đề nghị sử dụng sự trừu tượng hóa (xem mã ví dụ). :

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

Bây giờ chúng ta có thể thêm Bố:

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

con trai:

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

và bố đã gặp một người phụ nữ tốt bụng:

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

Hình như chúng ta có một gia đình, hãy nói cho cả thế giới biết tên của họ:

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

}

Đầu ra System.out: Tommy Nancy Dad
System.err giống như trên (chỉ có phông chữ màu đỏ)
Đầu ra JOption:
Tommy rồi
Nancy rồi
bố


1
OP liên quan đến việc thay đổi quyền truy cập vào thống kê. Do đó, "tôi" là một "cha" hoặc "con trai" chung chung - thứ không cần tạo ra cho mỗi người cha hoặc con trai và do đó là một động tĩnh.
RichieHH

Vâng, quyền của bạn, tôi đã không thấy rằng nó là tĩnh. Haha, điều đó sẽ thay đổi câu trả lời của tôi thành khoảng 100 dòng mã, nếu tôi trả lời. Cảm ơn vì đã ngẩng cao đầu
nckbrz

6

Điều này trông giống như một lỗ hổng thiết kế.

Xóa từ khóa tĩnh và đặt biến ví dụ trong hàm tạo. Bằng cách này, Son chỉ đặt biến thành một giá trị khác trong hàm tạo của mình.


1
Điều gì không đúng về điều này? Nếu biến thành viên 'tôi' được cho là quá mức trong thiết kế của bạn, thì patrick là giải pháp chính xác
Chii

Trên thực tế, nếu giá trị của tôi là như nhau cho mọi trường hợp, chỉ cần loại bỏ 'tĩnh' sẽ ổn. Việc khởi tạo không cần phải có trong constructor.
Nate Parsons

Đúng vậy, mặc dù về mặt kỹ thuật (theo mã byte) tôi nghĩ nó gần giống nhau ;-)
Patrick Cornelissen

4

Mặc dù đúng là các biến lớp chỉ có thể được ẩn trong các lớp con và không bị ghi đè, nhưng vẫn có thể thực hiện những gì bạn muốn mà không bị ghi đè printMe ()trong các lớp con và phản ánh là bạn của bạn. Trong mã dưới đây tôi bỏ qua xử lý ngoại lệ cho rõ ràng. Hãy lưu ý rằng tuyên bố menhư protecteddường như không có nhiều ý nghĩa trong bối cảnh này, vì nó sẽ được ẩn trong các lớp con ...

class Dad
  {
    static String me = "dad";

    public void printMe ()
      {
        java.lang.reflect.Field field = this.getClass ().getDeclaredField ("me");
        System.out.println (field.get (null));
      }
  }

Rất thú vị, Java.lang.reflect hả? ... +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();
}

... sẽ in "con trai".


Đây không giống như câu hỏi ban đầu sao?
Corley Brigman

Bạn có thể giải thích lý do tại sao ? (trong câu trả lời của bạn)
Mr_and_Mrs_D

3

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

Nó được gọi là Trường ẩn

Từ liên kết trên

Trong một lớp, một trường có cùng tên với một trường trong siêu lớp sẽ ẩn trường của siêu lớp, ngay cả khi các kiểu của chúng khác nhau. Trong lớp con, trường trong lớp bậc trên không thể được tham chiếu bằng tên đơn giản của nó. Thay vào đó, trường phải được truy cập thông qua siêu, được bao phủ trong phần tiếp theo. Nói chung, chúng tôi không khuyên bạn nên ẩn các trường vì nó làm cho mã khó đọc.


1
Liên kết đến một giải pháp tiềm năng luôn được chào đón, nhưng vui lòng thêm ngữ cảnh xung quanh liên kết để người dùng đồng nghiệp của bạn sẽ có một số ý tưởng về nó là gì và tại sao nó lại ở đó. Luôn trích dẫn phần có liên quan nhất của một liên kết quan trọng, trong trường hợp trang đích không thể truy cập được hoặc ngoại tuyến vĩnh viễn. Hãy xem xét rằng hầu như không có nhiều hơn một liên kết đến một trang web bên ngoài là một lý do có thể là Tại sao và làm thế nào một số câu trả lời bị xóa? .
FelixSFD

2

chỉ bằng cách ghi đè printMe():

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

tham chiếu metrong Dad.printMephương thức ngầm định trỏ đến trường tĩnh Dad.me, do đó, bằng cách này hay cách khác bạn đang thay đổi những gì printMelàm trong Son...


2

Bạn không thể ghi đè các biến trong một lớp. Bạn chỉ có thể ghi đè các phương thức. Bạn nên giữ các biến riêng tư nếu không bạn có thể gặp nhiều vấn đề.


Bạn không thể ghi đè bất kỳ số liệu thống kê.
Tom Hawtin - tackline

(hoặc ít nhất là nó không có ý nghĩa, IFSWIM.)
Tom Hawtin - tackline

Chính xác, bạn không thể ghi đè thống kê. Bạn có thể HIỂN THỊ họ hoặc một số người gọi tôi là MẶT NẠ.
Dale

2

Nó thực sự in 'cha', vì lĩnh vực này không bị ghi đè mà bị ẩn. Có ba cách tiếp cận để làm cho nó in 'con trai':

Cách tiếp cận 1: ghi đè 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();
}

Cách tiếp cận 2: không ẩn trường và khởi tạo nó trong hàm tạo

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

Cách tiếp cận 3: sử dụng giá trị tĩnh để khởi tạo một trường trong hàm tạo

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

2

Các biến không tham gia quá mức. Chỉ có phương pháp làm. Một cuộc gọi phương thức được giải quyết trong thời gian chạy, nghĩa là, quyết định gọi một phương thức được thực hiện trong thời gian chạy, nhưng các biến chỉ được quyết định tại thời điểm biên dịch. Do đó, biến đó được gọi có tham chiếu được sử dụng để gọi và không phải của đối tượng thời gian chạy.

Hãy xem đoạn trích sau:

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

}

Khi bạn chạy mã, giao diện điều khiển sẽ hiển thị:

Inside Honda
Inside Honda
Inside bike

Max_Speed = 150
Max_Speed = 90
Max_Speed = 90

0

Tất nhiên sử dụng các thuộc tính riêng tư, và getters và setters sẽ là điều nên làm, nhưng tôi đã thử nghiệm sau đây và nó hoạt động ... Xem nhận xét trong mã

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 ... tôi đã xác định lại các quy tắc thừa kế hay tôi đã đặt Oracle vào một tình huống khó khăn? Đối với tôi, chuỗi tĩnh được bảo vệ tôi rõ ràng bị ghi đè, như bạn có thể thấy khi bạn thực hiện chương trình này. Ngoài ra, nó không có ý nghĩa gì với tôi tại sao các thuộc tính không nên quá mức.


1
Trong trường hợp này, bạn đang "ẩn" biến khỏi siêu hạng. Điều này khác với ghi đè và không liên quan gì đến thừa kế. Các lớp khác sẽ thấy một biến hoặc biến khác tùy thuộc vào việc chúng tham chiếu lớp cơ sở hay lớp con và biến chúng nhìn thấy được cố định tại thời điểm biên dịch. Nếu bạn thay đổi tên "tôi" trong lớp phụ thành tên khác, bạn sẽ nhận được hiệu ứng tương tự. Hầu hết các IDE và công cụ xác thực mã (findbugs, v.v.) sẽ cảnh báo bạn khi bạn ẩn các biến như thế này vì nó thường không phải là điều bạn muốn làm.
AutomatedMike

Bạn đang xem xét lập trình từ quan điểm của mã hóa một mình. Đó là tốt, mã tuy nhiên bạn muốn trong trường hợp đó. Nếu bạn nhìn vào mã hóa từ quan điểm của một thành viên trong nhóm, thì các quy tắc sẽ trở nên rõ ràng, chẳng hạn như tại sao các trường không bị quá tải và v.v. Tôi có thể cho bạn một bài phát biểu về lý do tại sao, nhưng nếu bạn không nhìn thấy nó từ quan điểm đó của một thành viên trong nhóm, bạn sẽ chỉ thấy quan điểm của tôi là không hợp lệ và ném lại những lập luận không hợp lệ cho tôi.
nckbrz

0

Tại sao bạn muốn ghi đè các biến khi bạn có thể dễ dàng gán lại chúng trong các lớp con.

Tôi theo mô hình này để làm việc xung quanh thiết kế ngôn ngữ. Giả sử trường hợp bạn có một lớp dịch vụ có trọng số trong khung của bạn cần được sử dụng theo các hương vị khác nhau trong nhiều ứng dụng dẫn xuất. Trong trường hợp đó, cách tốt nhất để định cấu hình logic siêu lớp là bằng cách gán lại các biến 'xác định' của nó.

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

}

0

Không. Các biến lớp (Cũng áp dụng cho các biến thể hiện) không thể hiện tính năng ghi đè trong Java khi các biến lớp được gọi trên cơ sở loại đối tượng gọi. Đã thêm một lớp (Con người) trong hệ thống phân cấp để làm cho nó rõ ràng hơn. Vì vậy, bây giờ chúng tôi có

Con trai kéo dài Bố kéo dài con người.

Trong đoạn mã dưới đây, chúng tôi cố gắng lặp lại một loạt các đối tượng Con người, Bố và Con, nhưng nó in các giá trị của Lớp Người trong mọi trường hợp vì loại đối tượng gọi là Con người.

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

Sẽ in

  • Nhân loại
  • Nhân loại
  • Nhân loại

Vì vậy, không ghi đè các biến Class.

Nếu chúng ta muốn truy cập biến lớp của đối tượng thực tế từ một biến tham chiếu của lớp cha của nó, chúng ta cần nói rõ điều này với trình biên dịch bằng cách chuyển tham chiếu cha (đối tượng Human) sang kiểu của nó.

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

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

Sẽ in

  • cha
  • Con trai

Về phần của câu hỏi này: - Như đã đề xuất, ghi đè phương thức printMe () trong lớp Son, sau đó gọi

Son().printMe();

Biến "Dad" Class của cha sẽ bị ẩn bởi vì khai báo gần nhất (từ phương thức printme () của lớp Son) của "tôi" (trong lớp Son) sẽ được ưu tiên.


0

Chỉ cần gọi super.variable trong hàm tạo của lớp con

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

}

}

Đầu ra là 10.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.