2012-05-23 32 views
8

Xem ví dụ sau:Tại sao không thể quá tải được thực hiện tại thời gian truy tố?

interface I {} 

class A implements I {} 

class B implements I {} 

class Foo{ 
    void f(A a) {} 
    void f(B b) {} 
    static public void main(String[]args) { 
     I[] elements = new I[] {new A(), new B(), new B(), new A()}; 
     Foo o = new Foo(); 
     for (I element:elements) 
      o.f(element);//won't compile 
    } 
} 

Tại sao phương pháp nạp chồng không hỗ trợ upcasting?

Nếu quá tải được thực hiện trong thời gian chạy, nó sẽ cung cấp sự linh hoạt hơn nhiều. Ví dụ: Mẫu khách truy cập sẽ đơn giản hơn. Có lý do kỹ thuật nào ngăn Java thực hiện việc này không?

+0

Tôi đang cố gắng tưởng tượng cách ngữ nghĩa sẽ hoạt động theo cách tiếp cận này và bộ não của tôi chỉ "ew". Tôi mong đợi để có thể thay đổi việc thực hiện tôi đang sử dụng một giao diện, và có phần còn lại của mã hoạt động chính xác như nhau. –

+0

Bởi vì cố gắng cố gắng để xác định kiểu thời gian chạy của một đối tượng sẽ giống như chỉ đơn giản là chạy chương trình? – Perception

+1

@Perception Anh ấy hỏi lý do tại sao quá tải công văn không thể được trì hoãn để chạy thay vì giải quyết tại thời gian biên dịch. Điều đó nghe có vẻ như nó sẽ thêm phức tạp rất lớn vào JVM và logic tải lớp. –

Trả lời

1

phương pháp của bạn cho biết sẽ chấp nhận A hoặc B lớp mà có nguồn gốc của I, chúng có thể chứa thêm chi tiết sau đó I

void f(A a) {} 

Khi bạn cố gắng gửi siêu lớp của A trong trường hợp của bạn giao diện I, trình biên dịch muốn xác nhận rằng bạn đang thực sự gửi A vì chi tiết có sẵn trong A có thể không có sẵn trong I, cũng chỉ trong thời gian chạy I thực sự sẽ tham chiếu đến một phiên bản A không có thông tin như vậy tại com đống thời gian, vì vậy bạn sẽ phải nói rõ ràng với trình biên dịch rằng I thực sự là A hoặc B và bạn thực hiện một phép để nói như vậy.

+0

Tôi không thể hiểu rõ điều này, nhưng nếu mục đích là nói rằng những gì OP muốn không thể được thực hiện, nó có thể. – EJP

6

Độ phân giải quá tải liên quan đến một số quy tắc không tầm thường để xác định xem tình trạng quá tải nào phù hợp nhất và rất khó để thực hiện những việc này một cách hiệu quả khi chạy. Ngược lại, độ phân giải ghi đè dễ dàng hơn - trong trường hợp khó, bạn chỉ cần tra cứu hàm foo cho lớp của đối tượng và trong trường hợp dễ dàng (ví dụ: khi chỉ có một triển khai thực hiện hoặc chỉ một triển khai trong đường dẫn mã này), bạn có thể biến phương thức ảo thành một cuộc gọi được biên dịch tĩnh, phi ảo, không động, (nếu bạn làm nó dựa trên đường dẫn mã, bạn phải kiểm tra nhanh để xác minh rằng đối tượng thực sự là một trong những bạn mong đợi).

Khi nó quay ra, đó là một điều tốt Java 1.4 và thấp hơn không có độ phân giải ghi đè thời gian chạy, bởi vì điều đó sẽ làm cho generics khó khăn hơn để trang bị thêm. Generics đóng một vai trò trong độ phân giải ghi đè, nhưng thông tin này sẽ không có sẵn trong thời gian chạy do xóa.

2

Không phải là câu trả lời cho Java. Chức năng này tồn tại trong C# 4 mặc dù:

using System; 

public class MainClass {  
    public static void Main() { 
     IAsset[] xx = { 
      new Asset(), new House(), new Asset(), new House(), new Car() 
     };  
     foreach(IAsset x in xx) { 
      Foo((dynamic)x); 
     } 
    } 


    public static void Foo(Asset a) { 
     Console.WriteLine("Asset"); 
    } 

    public static void Foo(House h) { 
     Console.WriteLine("House"); 
    } 

    public static void Foo(Car c) { 
     Console.WriteLine("Car"); 
    }  
} 

public interface IAsset { }  

public class Asset : IAsset { } 

public class House : Asset { } 

public class Car : Asset { } 

Output:

Asset 
House 
Asset 
House 
Car 

Nếu bạn đang sử dụng C# 3 và dưới đây, bạn phải sử dụng phản chiếu, tôi đã thực hiện một bài về nó trên blog của tôi Nhiều Công văn trong C#: http://www.ienablemuch.com/2012/04/multiple-dispatch-in-c.html

Nếu bạn muốn thực hiện nhiều công văn bằng Java, bạn có thể đi theo tuyến phản chiếu.

Đây là một giải pháp cho Java: http://blog.efftinge.de/2010/03/multiple-dispatch-and-poor-mens-patter.html

3

Tôi không nghĩ rằng bất cứ ai nhưng các nhà thiết kế của ngôn ngữ có thể có thể trả lời câu hỏi này. Tôi không phải là một chuyên gia về chủ đề này, nhưng tôi sẽ chỉ đưa ra ý kiến ​​của tôi.

Bằng cách đọc JLS 15.12 về Biểu thức yêu cầu phương pháp, rõ ràng là việc chọn phương thức đúng để thực thi là một quá trình biên dịch đã phức tạp; trên tất cả sau khi giới thiệu generics.

Bây giờ hãy tưởng tượng di chuyển tất cả điều này sang thời gian chạy chỉ để hỗ trợ tính năng duy nhất của mutimethods. Đối với tôi, nó có vẻ giống như một tính năng nhỏ làm tăng thêm độ phức tạp cho ngôn ngữ và có thể là một tính năng với một số tác động hiệu quả nhất định mà tất cả các quyết định này sẽ cần phải được thực hiện, lặp đi lặp lại, trong thời gian chạy và không chỉ một lần, như ngày nay, tại thời điểm biên dịch.

Để tất cả những điều này, chúng tôi có thể thêm thực tế là do type erasure sẽ không thể xác định loại thực tế của một số loại chung nhất định. Dường như với tôi rằng việc bỏ qua sự an toàn của việc kiểm tra kiểu tĩnh không phải là lợi ích tốt nhất của Java.

Ở mức độ nào, có các lựa chọn thay thế hợp lệ để xử lý vấn đề nhiều công văn, và có lẽ các giải pháp thay thế này khá nhiều biện minh tại sao nó chưa được triển khai bằng ngôn ngữ. Vì vậy, bạn có thể sử dụng cổ điển visitor pattern hoặc bạn có thể sử dụng một số lượng phản ánh nhất định.

Có lỗi MultiJava Project đã triển khai mutiple gửi hỗ trợ trong Java và có một vài dự án khác ngoài đó sử dụng sự phản chiếu để hỗ trợ đa phương thức trong Java: Java Multimethods, Java Multimethods Framework. Có lẽ còn nhiều hơn nữa.

Bạn cũng có thể xem xét ngôn ngữ dựa trên Java thay thế hỗ trợ đa phương thức, như Clojure hoặc Groovy.

Ngoài ra, vì C# là một ngôn ngữ khá giống với Java trong phillosopy nói chung của nó, nó có thể là thú vị để điều tra thêm về cách nó supports multimethods và suy ngẫm về những gì sẽ là những tác động của việc cung cấp một tính năng tương tự trong Java. Nếu bạn nghĩ rằng đó là một tính năng đáng có trong Java, bạn thậm chí có thể gửi một JEP và nó có thể được tính đến cho các phiên bản tương lai của ngôn ngữ Java.

4

Không có lý thuyết lý do tại sao không thể thực hiện được. Hệ thống đối tượng chung của Lisp hỗ trợ kiểu xây dựng này được gọi là nhiều công văn - mặc dù nó làm như vậy trong một mô hình hơi khác (phương pháp, chứ không phải là gắn liền với đối tượng, là trường hợp của generics (hoặc các hàm chung). làm công văn ảo tại thời gian chạy trên các giá trị của nhiều tham số). Tôi tin rằng cũng đã có phần mở rộng để Java để kích hoạt nó (Multi-Java đến với tâm trí, mặc dù đó có thể đã được thừa kế nhiều hơn là nhiều công văn). Tuy nhiên, có thể có các lý do ngôn ngữ Java tại sao nó không thể được thực hiện, bên cạnh các nhà thiết kế ngôn ngữ chỉ nghĩ rằng nó không nên được thực hiện, rằng tôi sẽ để người khác để lý luận về. Tuy nhiên, nó đưa ra các biến chứng cho thừa kế. Hãy xem xét:

interface A {} 
interface B {} 
class C implements A {} 

class Foo { 
    public void invoke(A a) {} 
    public void invoke(B b) {} 
} 

class Bar extends Foo { 
    public void invoke(C c) {} 
} 

class Baz extends Bar { 
    public void invoke(A a) {} 
} 

Baz obj = new Baz(); 
obj.invoke(new C); 

Số nào được gọi là invoke? Baz? Bar? super.invoke là gì? Có thể đưa ra các ngữ nghĩa xác định, nhưng chúng có khả năng sẽ gây nhầm lẫn và ngạc nhiên trong ít nhất một số trường hợp. Do Java hướng tới là một ngôn ngữ đơn giản, tôi không nghĩ rằng các tính năng giới thiệu sự nhầm lẫn đó có thể được xem như là theo các mục tiêu của nó.

2

đoán bạn chỉ cần phải giải quyết với sự phản ánh:

import java.lang.reflect.*; 

interface I {}  
class A implements I {}  
class B implements I {} 

public class Foo { 

    public void f(A a) { System.out.println("from A"); } 
    public void f(B b) { System.out.println("from B"); } 

    static public void main(String[]args) throws InvocationTargetException 
     , NoSuchMethodException, IllegalAccessException 
    { 
     I[] elements = new I[] {new A(), new B(), new B(), new A()}; 
     Foo o = new Foo(); 
     for (I element : elements) { 
      o.multiDispatch(element);  
     } 
    } 

    void multiDispatch(I x) throws NoSuchMethodException 
     , InvocationTargetException, IllegalAccessException 
    {  
     Class cls = this.getClass(); 

     Class[] parameterTypes = { x.getClass() }; 
     Object[] arguments = { x }; 

     Method fMethod = cls.getMethod("f", parameterTypes); 
     fMethod.invoke(this,arguments); 
    }  
} 

Output:

from A 
from B 
from B 
from A 
3

Có bất kỳ lý do kỹ thuật ngăn Java từ việc này?

Mã đúng đắn: ví dụ hiện tại của bạn cung cấp hai cách triển khai I và 2 phương pháp tương ứng f. Tuy nhiên không có gì ngăn cản sự tồn tại của các lớp khác thực hiện I - di chuyển độ phân giải đến thời gian chạy cũng sẽ thay thế các lỗi biên dịch thành các lỗi thời gian chạy ẩn có thể.

Hiệu suất: như những người khác đã đề cập đến quá tải phương thức liên quan đến các quy tắc khá phức tạp, làm như vậy một lần tại thời gian biên dịch chắc chắn là nhanh hơn làm cho mọi lời gọi phương thức trong thời gian chạy.

Khả năng tương thích ngược: phương pháp hiện đang bị quá tải được giải quyết bằng cách sử dụng loại thời gian biên dịch của đối số được chuyển thay vì loại thời gian chạy của chúng, thay đổi hành vi sử dụng thông tin thời gian chạy sẽ phá vỡ rất nhiều ứng dụng hiện có.

Làm thế nào để làm việc xung quanh nó

Sử dụng mẫu người truy cập, tôi không hiểu tại sao ai đó sẽ nghĩ rằng nó rất khó.

interface I{ 
     void accept(IVisitor v); 
} 
interface IVisitor{ 
     void f(A a); 
     void f(B b); 
} 

class A implements I{ 
    void accept(IVisitor v){v.f(this);} 
} 
class B implements I{ 
    void accept(IVisitor v){v.f(this);} 
} 
class Foo implements IVisitor{ 
void f(A a) {} 
void f(B b) {} 
static public void main(String[]args) { 
    I[] elements = new I[] {new A(), new B(), new B(), new A()}; 
    Foo o = new Foo(); 
    for (I element:elements) 
     element.accept(o); 
} 


} 
Các vấn đề liên quan