2009-01-01 16 views
14
public class CovariantTest { 
    public A getObj() { 
     return new A(); 
    } 

    public static void main(String[] args) { 
     CovariantTest c = new SubCovariantTest(); 
     System.out.println(c.getObj().x); 
    } 
} 

class SubCovariantTest extends CovariantTest { 
    public B getObj() { 
     return new B(); 
    } 
} 

class A { 
    int x = 5; 
} 

class B extends A { 
    int x = 6; 
} 

Mã trên in 5 khi được biên dịch và chạy. Nó sử dụng sự trả về biến đổi cho phương thức over-ridden.Java Covariants

Tại sao nó in 5 thay vì 6, vì nó thực thi phương thức overOridden getObj trong lớp SubCovariantTest.

Một số người có thể ném một số ánh sáng về điều này. Cảm ơn.

Trả lời

15

Điều này là do trong các biến thành viên Java không ghi đè, chúng bóng (không giống như phương pháp). Cả A và B đều có biến x. Vì c là được khai báo thuộc loại CovarientTest, sự trở lại của getObj() hoàn toàn là A, không phải B, vì vậy bạn nhận được A's x, chứ không phải B's x.

11

Java không ghi đè trường (còn gọi là thuộc tính hoặc biến thành viên). Thay vào đó, họ shadow over lẫn nhau. Nếu bạn chạy chương trình thông qua trình gỡ rối, bạn sẽ tìm thấy hai biến số x trong bất kỳ đối tượng nào thuộc loại B.

Dưới đây là giải thích về những gì đang xảy ra. Chương trình đầu tiên lấy một cái gì đó là ngầm của loại A và sau đó gọi cho x được giả định đến từ A. Mặc dù nó rõ ràng là một loại phụ, trong ví dụ của bạn một đối tượng kiểu B được tạo thông qua SubCovariantTest, nó vẫn giả định bạn trả về một cái gì đó trong getObj() được ngầm nhập A. Vì Java không thể ghi đè các trường, kiểm tra sẽ gọi A.x và không B.x.

CovariantTest c = new SubCovariantTest(); 
// c is assumed the type of CovariantTest as it is 
// implicitly declared 

System.out.println(c.getObj().x); 
// In this method chain the following happens: 

// c.getObj() will return object of type B 
// BUT will assume it is an A 

// c.getObj().x will return the x from A 
// since in this context the compiler assumes 
// it is an A and make the call to A.x 

Nó có vẻ như một dấu hiệu bướng bỉnh vì các phương thức luôn được ghi đè trong Java (so với C++ và C# trong đó chúng không có). Bạn thường không gặp phải vấn đề này vì quy ước mã Java cho bạn biết không bao giờ truy cập trực tiếp vào các trường. Thay vào đó hãy chắc chắn rằng các lĩnh vực luôn luôn truy cập thông qua phương pháp accessor, tức là getters:

class A { 
    private int x = 5; 

    public int getX() { // <-- This is a typical accessor method 
     return x; 
    } 
} 

class B extends A { 
    private int x = 6; 

    @override 
    public int getX() { 
     // will be called instead even though B is implied to be A 
     // @override is optional because methods in Java are always virtual 
     // thus are always overridden 
     return x; 
    } 
} 

Mã để làm việc này là như sau:

c.getObj().getX(); 
// Will now call getX() in B and return the x that is defined in B's context. 
10

Thay thế A và B của bạn ở trên với:

class A { 
    public int getX() { return 5; } 
} 

class B extends A { 
    public int getX() { return 6; } 
} 

Điều đó có thể sẽ trả lời câu hỏi của bạn về những gì sai ;-)

9

Có hai trường có tên x trong đối tượng, một từ lớp A và một từ lớp B, để ẩn một trường trong A. Trường x được gọi là trường trong A, vì khai báo của c .

Trên thực tế đây không phải là một vấn đề, bởi vì nó là phong cách rất xấu để

  • giấu một trường trong một lớp con,

  • truy cập một lĩnh vực trực tiếp thay vì thông qua một phương pháp.

1

Tôi đã xem mã của bạn và gặp sự cố khi biên dịch. Nhận lỗi

Loại trả về không tương thích với CovariantTest.getObj()

Tôi đã thực hiện một thay đổi nhỏ.

class A { 
    int x = 5; 
} 

class B extends A { 
    int x = 6; 
} 

public class CovariantTest { 

    public A getObj() { 
     return new A(); 
    } 

    public static void main(String[] args) { 
     CovariantTest c = new SubCovariantTest(); 
     A a = c.getObj(); 

     System.out.println(c.getObj().x); 
    } 
} 

class SubCovariantTest extends CovariantTest { 
    public A getObj() { 
     return new B(); 
    } 

} 

Gắn điểm ngắt tại đường ra của hệ thống và xem biến. Nó chứa hai x thành viên, với một bộ thành 5 và một đến 6.

Câu trả lời của Starblue giải thích hành vi này.

0

c được nhập là CovariantTest tại thời gian biên dịch và do đó lệnh gọi tới c.getObj() được liên kết với phương thức CovariantTest.getObj() tại thời gian biên dịch (và không thể sửa đổi khi chạy).

Ngoài ra, x tồn tại trong cả A và B (nó bị che khuất, không bị ghi đè). Bởi vì phương pháp được gọi là CovariantTest.getObj() và phương pháp làm việc với A, x được lấy ra là Axe mặc dù các đối tượng thực tế là loại B.

2
package ch2; 

class CovariantTest 
{ 
    public A getObj() 
    { 
     return new A(); 
    } 
} 

class SubCovariantTest extends CovariantTest 
{ 
    public B getObj() 
    { 
     return new B(); 
    } 
} 

public class TestPrg 
{ 
    public static void main(String[] args) 
    { 
     CovariantTest c = new SubCovariantTest(); 
     System.out.println("c.getObj().x :: "+c.getObj().x); 
     System.out.println("c.getObj().getX() :: "+c.getObj().getX()); 
    } 
} 

class A 
{ 
    int x = 5; 

    int getX() 
    { 
     return x; 
    } 
} 

class B extends A 
{ 
    int x = 6; 

    int getX() 
    { 
     return x; 
    } 
} 

đơn giản ... Polymorphism là chỉ áp dụng cho chức năng. Không phải biến.

Biến sẽ được giải quyết trong thời gian biên dịch.