2013-08-09 51 views
7

Hãy tha thứ cho tôi nếu câu hỏi này chủ yếu dựa trên ý kiến, nhưng tôi có cảm giác rằng nó không phải là và có một lý do chính đáng cho sự lựa chọn. Đây là một ví dụ. Xin lỗi, nó thực sự dài, nhưng siêu đơn giản:Giải thích biên dịch ghi đè và quá tải

Interface:

public interface Shape 
{ 
    double area(); 
} 

Thực hiện lớp 1:

import static java.lang.Math.PI; 

public class Circle implements Shape 
{ 
    private double radius; 

    public Circle(double radius) 
    { 
     this.radius = radius; 
    } 

    public double area() 
    { 
     return PI*radius*radius; 
    } 
} 

Thực hiện lớp 2:

public class Square implements Shape 
{ 
    private double size; 

    public Square(double sideLength) 
    { 
     size = sideLength; 
    } 

    public double area() 
    { 
     return size*size; 
    } 
} 

Driver:

Shape[] shapes = new Shape[]{new Circle (5.3), new Square (2.4)}; 

System.out.println(shapes[0].area()); //prints 88.247... 
System.out.println(shapes[1].area()); //prints 5.76 

Công trình này từ .area() bị ghi đè bởi CircleSquare. Bây giờ, đây là câu hỏi của tôi thực sự bắt đầu. Hãy nói rằng người lái xe có những phương pháp:

public static void whatIs(Shape s) 
{ 
    System.out.println("Shape"); 
} 

public static void whatIs(Circle s) 
{ 
    System.out.println("Circle"); 
} 

public static void whatIs(Square s) 
{ 
    System.out.println("Square"); 
} 

Nếu chúng ta gọi là:

whatIs(shapes[0]); //prints "Shape" 
whatIs(shapes[1]); //prints "Shape" 

Điều này xảy ra bởi vì Java giải thích các đối tượng như Shape s và không CircleSquare. Dĩ nhiên chúng ta có thể có được kết quả mong muốn thông qua:

if (shapes[0] instanceof Circle) 
{ 
    whatIs((Circle) shapes[0]); //prints "Circle" 
} 
if (shapes[1] instanceof Square) 
{ 
    whatIs((Square) shapes[1]); //prints "Square" 
} 

Bây giờ chúng ta có một nền tảng câu hỏi của tôi là: "Shape"
gì lý do góp phần vào việc thiết kế trình biên dịch/ngôn ngữ mà whatIs(shapes[0]); sẽ in Như trong, tại sao trình biên dịch Java có thể phân biệt chính xác giữa các phương thức ghi đè cho các đối tượng liên quan, nhưng không phải là các phương thức quá tải? Cụ thể hơn, nếu các phương pháp duy nhất mà tài xế phải truy cập là:

public static void whatIs(Circle s) 
{ 
    System.out.println("Circle"); 
} 
public static void whatIs(Square s) 
{ 
    System.out.println("Square"); 
} 

và chúng tôi cố gắng để gọi,

whatIs(shapes[0]); 
whatIs(shapes[1]); 

chúng tôi sẽ nhận được hai lỗi (một cho Square và một cho Circle) cho thấy rằng:

  • phương pháp Driver.whatIs (Square) không áp dụng
    • Shape tranh luận thực tế không thể được chuyển đổi sang Quảng trường bằng cách chuyển đổi phương pháp gọi

Vì vậy, một lần nữa, bây giờ mà chúng tôi đã nhận được đến nitty-gritty, tại sao có thể Java không xử lý một tình huống như thế điều này? Như trong, điều này được thực hiện do mối quan tâm hiệu quả, là nó chỉ là không thể do một số quyết định thiết kế, đây có phải là một thực hành xấu vì một lý do nào đó, vv?

+0

Ngoài ra, nếu có ai có ý tưởng tốt hơn cho tiêu đề, vui lòng đưa ra đề xuất hoặc chỉnh sửa, tôi biết rằng đó có thể không phải là đề xuất hay nhất, nhưng không thể nghĩ ra điều gì tốt hơn. Cảm ơn tất cả những ai đã vượt qua bài đăng này! –

+4

Tôi đề nghị bạn đi qua [Phần JLS 15.2.2] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2) (Quá tải) và [JLS Mục 8.4.8.1] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8.1) (Ghi đè) –

+1

Câu hỏi thú vị và @ RohitJain cảm ơn vì lời bình luận, tôi cũng sẽ phải đọc những lời đó. –

Trả lời

4

Tại sao trình biên dịch Java có thể phân biệt chính xác giữa các phương thức ghi đè cho các đối tượng liên quan, nhưng không phải là các phương thức quá tải?

Không thể.

Kiểm tra nghiêm ngặt theo loại có thể thấy bảo đảm &. Nếu mã của bạn là shapes[0].area(), nó sẽ kiểm tra xem Shape có phương thức area và sẽ biên dịch nó thành "khu vực cuộc gọi() trên đối tượng đó" hay không. Đối tượng cụ thể tồn tại trong thời gian chạy được đảm bảo có phương thức đó. Phiên bản nào từ lớp nào thực sự được sử dụng được giải quyết động trong thời gian chạy.

Các phương thức gọi quá tải hoạt động giống nhau. Trình biên dịch nhìn thấy Shape và biên dịch thành "gọi whatis() trong phiên bản Hình dạng cơ bản". Nếu bạn muốn thay đổi điều đó (và thậm chí cho phép không có phiên bản Shape cơ bản), bạn sẽ cần phải có khả năng xác định loại tại thời gian biên dịch.

Nhưng AFAIK không thể tạo trình biên dịch có thể xác định loại đối tượng sẽ có lúc chạy tại thời điểm đó. Hãy nghĩ ví dụ:

final Shape[] shapes = new Shape[] { new Circle(5.3), new Square(2.4) }; 
    new Thread() { 
     public void run() { 
      shapes[0] = new Square(1.5); 
     } 
    }.start(); 
    whatIs(shapes[0]); 

Bạn phải thực thi mã đó để tìm hiểu.

Trình biên dịch có thể tự động tạo mã như

if (shapes[0] instanceof Circle) 
{ 
    whatIs((Circle) shapes[0]); //prints "Circle" 
} 

để bạn có thể đạt được gọi phương pháp năng động trong thời gian chạy nhưng nó không. Tôi không biết lý do nhưng đôi khi nó sẽ gọn gàng. Mặc dù instanceof thường là một dấu hiệu cho thiết kế lớp xấu - bạn không nên nhìn từ bên ngoài cho sự khác biệt, để cho các lớp hoạt động khác nhau để bên ngoài không cần phải biết.

+0

Bây giờ tôi đoán câu hỏi của tôi giống như câu hỏi của bạn, vì tại sao trình biên dịch không tự động tạo mã trong ví dụ cuối cùng. Tuy nhiên, bạn đã trả lời câu hỏi ban đầu của tôi. Cảm ơn bạn. –

2

Công văn đến một trong các phương pháp whatIs được quyết định bởi trình biên dịch tại thời gian biên dịch. Cuộc gọi đến một trong các phương thức area được quyết định trong thời gian chạy, dựa trên lớp thực tế của đối tượng được tham chiếu.

5

Java, với tính năng hướng đối tượng, hỗ trợ đa hình, do đó, hãy gọi area sẽ gọi phương thức area của trường hợp cụ thể, bất kể nó là gì. Điều này được xác định trong thời gian chạy.

Tuy nhiên, tính đa hình này không được hỗ trợ với các phương thức quá tải. Các Java Language Specification, Section 8.4.9 diện này:

Khi một phương pháp được gọi (§15.12), số lượng các đối số thực tế (và bất kỳ đối số kiểu tường minh) và các loại thời gian biên dịch của lập luận được sử dụng, tại thời gian biên dịch , để xác định chữ ký của phương thức sẽ được gọi (§15.12.2). Nếu phương thức được gọi là được gọi là phương thức thể hiện, phương thức thực tế được gọi sẽ là được xác định tại thời gian chạy, sử dụng tra cứu phương pháp động (§15.12.4).

Tức là, với các phương thức quá tải, phương pháp được chọn tại thời gian biên dịch, sử dụng các loại thời gian biên dịch của các biến, chứ không phải trong thời gian chạy như với đa hình.

1

Hỏi: Tại sao trình biên dịch Java phân biệt chính xác giữa các phương thức ghi đè cho các đối tượng liên quan, nhưng không quá tải phương thức ... tại sao Java không xử lý được tình huống như thế này?

A: Bạn đã có câu hỏi ngược.

Java ALLOWS bạn để phân biệt giữa "quá tải" và "ghi đè".

Nó không cố gắng để đoán thứ hai bạn có nghĩa là, nó mang lại cho bạn một sự lựa chọn để sử dụng một hoặc khác.

1

Vâng, như một câu trả lời ngu ngốc, bạn có thể nhận được các chức năng whatis để làm việc tốt theo cách này (mà không cần bất kỳ loại kiểm tra)

class Shape{ 
    public abstract String whatIs(); 
} 

class Square{ 
    public String whatIs(){ return "Square"; } 
} 
class Circle{ 
    public String whatIs(){ return "Circle"; } 
} 

Và sau đó gọi cho họ như thế này

Shape square = new Square(); 
Shape circle = new Circle(); 

System.out.println(square.whatIs()) //prints 'square' 
System.out.println(circle.whatIs()) //prints 'circle 

Không hề tất cả câu trả lời cho câu hỏi bạn hỏi ... Nhưng tôi không thể cưỡng lại được.

+0

Bạn chính xác ở chỗ đây không phải là những gì tôi đã hỏi, nhưng dù sao, nó thú vị và tôi chắc chắn nó sẽ giúp một ai đó. +1 –