Là phương pháp quá tải bất cứ điều gì nhiều hơn cú pháp đường? [đóng cửa]


19

Là phương pháp quá tải một loại đa hình? Đối với tôi có vẻ như chỉ đơn giản là sự khác biệt của các phương thức có cùng tên và các tham số khác nhau. Vì vậy, stuff(Thing t)stuff(Thing t, int n)là các phương thức hoàn toàn khác nhau khi có liên quan đến trình biên dịch và thời gian chạy.

Nó tạo ra ảo ảnh, về phía người gọi, đó là cùng một phương thức hoạt động khác nhau trên các loại đối tượng khác nhau - đa hình. Nhưng đó chỉ là ảo ảnh, bởi vì thực sự stuff(Thing t)stuff(Thing t, int n)là những phương pháp hoàn toàn khác nhau.

Là phương pháp quá tải bất cứ điều gì nhiều hơn cú pháp đường? Tui bỏ lỡ điều gì vậy?


Một định nghĩa phổ biến cho đường cú pháp, là nó hoàn toàn cục bộ . Có nghĩa là thay đổi một đoạn mã thành tương đương 'ngọt ngào' hoặc ngược lại, liên quan đến những thay đổi cục bộ không ảnh hưởng đến cấu trúc chung của chương trình. Và tôi nghĩ rằng quá tải phương pháp chính xác phù hợp với tiêu chí này. Hãy xem xét một ví dụ để chứng minh:

Hãy xem xét một lớp học:

class Reader {
    public String read(Book b){
        // .. translate the book to text
    }
    public String read(File b){
        // .. translate the file to text
    }
}

Bây giờ hãy xem xét một lớp khác sử dụng lớp này:

/* might not be the best example */
class FileProcessor {
    Reader reader = new Reader();
    public void process(File file){
        String text = reader.read(file);
        // .. do stuff with the text
    }
}

Đuợc. Bây giờ hãy xem những gì cần thay đổi nếu chúng ta thay thế quá tải phương thức bằng các phương thức thông thường:

Các readphương pháp Readerthay đổi thành readBook(Book)readFile(file). Chỉ có một vấn đề thay đổi tên của họ.

Mã gọi trong FileProcessorthay đổi một chút: reader.read(file)thay đổi thành reader.readFile(file).

Và đó là nó.

Như bạn có thể thấy, sự khác biệt giữa việc sử dụng nạp chồng phương thức và không sử dụng nó, hoàn toàn cục bộ . Và đó là lý do tại sao tôi nghĩ rằng nó đủ điều kiện là đường cú pháp tinh khiết.

Tôi muốn nghe phản đối của bạn nếu bạn có một số, có thể tôi đang thiếu một cái gì đó.


48
Cuối cùng, bất kỳ tính năng ngôn ngữ lập trình nào cũng chỉ là đường cú pháp cho trình biên dịch thô.
Philipp

31
@Philipp: Xin lỗi, nhưng đó là một tuyên bố thực sự ngu ngốc. Ngôn ngữ lập trình xuất phát từ tính hữu dụng của chúng từ ngữ nghĩa chứ không phải cú pháp. Các tính năng như một hệ thống loại cung cấp cho bạn sự đảm bảo thực tế, mặc dù chúng thực sự có thể yêu cầu bạn viết nhiều hơn .
back2dos

3
Hãy tự hỏi mình điều này: Có phải nhà điều hành quá tải chỉ là cú pháp đường? Bất cứ câu trả lời nào cho câu hỏi mà bạn giữ đúng cũng là câu trả lời cho câu hỏi bạn đã hỏi;)
back2dos

5
@ back2dos: Hoàn toàn đồng ý với bạn. Tôi đọc câu "mọi thứ chỉ là cú pháp cú pháp cho trình biên dịch" quá thường xuyên, và nó rõ ràng là sai. Đường cú pháp là một cú pháp thay thế (có thể đẹp hơn) cho một số cú pháp hiện có không thêm bất kỳ ngữ nghĩa mới nào.
Giorgio

6
@Giorgio: đúng rồi! Có một định nghĩa chính xác trong bài viết mang tính bước ngoặt của Matthias Felleisen về tính biểu cảm. Về cơ bản: đường cú pháp là hoàn toàn cục bộ. Nếu bạn phải thay đổi cấu trúc toàn cầu của chương trình để loại bỏ việc sử dụng tính năng ngôn ngữ, thì đó không phải là cú pháp cú pháp. Tức là viết lại mã OO đa hình trong trình biên dịch chương trình thường bao gồm thêm logic gửi toàn cầu, không hoàn toàn cục bộ, do đó OO không "chỉ là đường cú pháp cho trình biên dịch".
Jörg W Mittag

Câu trả lời:


29

Để trả lời điều này, trước tiên bạn cần một định nghĩa cho "đường cú pháp". Tôi sẽ đi với Wikipedia :

Trong khoa học máy tính, cú pháp đường là cú pháp trong một ngôn ngữ lập trình được thiết kế để làm cho mọi thứ dễ đọc hoặc diễn đạt hơn. Nó làm cho ngôn ngữ "ngọt ngào" hơn đối với việc sử dụng của con người: mọi thứ có thể được thể hiện rõ ràng hơn, chính xác hơn hoặc theo một phong cách khác mà một số người có thể thích.

[...]

Cụ thể, một cấu trúc trong một ngôn ngữ được gọi là đường cú pháp nếu nó có thể được loại bỏ khỏi ngôn ngữ mà không ảnh hưởng đến những gì ngôn ngữ có thể làm

Vì vậy, theo định nghĩa này, các tính năng như varargs của Java hoặc Scala để hiểu là đường cú pháp: chúng dịch sang các tính năng ngôn ngữ cơ bản (một mảng trong trường hợp đầu tiên, gọi tới map / Flatmap / bộ lọc trong lần thứ hai) và loại bỏ chúng không thay đổi những điều bạn có thể làm với ngôn ngữ.

Tuy nhiên, quá tải phương thức không phải là cú pháp cú pháp theo định nghĩa này, bởi vì loại bỏ nó sẽ làm thay đổi căn bản ngôn ngữ (bạn không còn có thể gửi đến hành vi khác biệt dựa trên các đối số).

Đúng, bạn có thể mô phỏng quá tải phương thức miễn là bạn có một số cách để truy cập các đối số của phương thức và có thể sử dụng cấu trúc "nếu" dựa trên các đối số mà bạn đưa ra. Nhưng nếu bạn xem xét đường cú pháp đó, bạn sẽ phải xem xét bất cứ thứ gì ở trên máy Turing để tương tự là đường cú pháp.


22
Loại bỏ quá tải sẽ không thay đổi những gì ngôn ngữ có thể làm. Bạn vẫn có thể làm chính xác những điều tương tự như trước đây; bạn chỉ cần đổi tên một vài phương thức. Đó là một thay đổi nhỏ hơn so với các vòng lặp desugaring.
Doval

9
Như tôi đã nói, bạn có thể sử dụng cách tiếp cận rằng tất cả các ngôn ngữ (bao gồm cả ngôn ngữ máy) chỉ đơn giản là cú pháp cú pháp trên đầu máy Turing.
kdgregory

9
Như tôi thấy, phương thức nạp chồng chỉ đơn giản là cho phép bạn làm sum(numbersArray)sum(numbersList)thay vì sumArray(numbersArray)sumList(numbersList). Tôi đồng ý với Doval, nó có vẻ như chỉ là đường tổng hợp.
Manila Cohn

3
Hầu hết các ngôn ngữ. Hãy thử thực hiện instanceof, các lớp học, thừa kế, giao diện, Generics, phản ánh hoặc specifiers truy cập sử dụng if, whilevà các toán tử logic, với ngữ nghĩa chính xác cùng . Không có trường hợp góc. Lưu ý rằng tôi không thách thức bạn tính toán những điều tương tự như việc sử dụng cụ thể của các cấu trúc đó. Tôi đã biết bạn có thể tính toán bất cứ điều gì bằng cách sử dụng logic boolean và phân nhánh / lặp. Tôi yêu cầu bạn triển khai các bản sao hoàn hảo về ngữ nghĩa của các tính năng ngôn ngữ đó, bao gồm mọi bảo đảm tĩnh mà chúng cung cấp (kiểm tra thời gian biên dịch vẫn phải được thực hiện vào thời gian biên dịch.)
Doval

6
@Doval, kdgregory: Để xác định đường cú pháp, bạn phải xác định nó liên quan đến một số ngữ nghĩa. Nếu ngữ nghĩa duy nhất bạn có là "Chương trình này tính toán gì?", Thì rõ ràng mọi thứ chỉ là cú pháp đường cho máy Turing. Mặt khác, nếu bạn có một ngữ nghĩa trong đó bạn có thể nói về các đối tượng và các thao tác nhất định trên chúng, thì việc xóa một số cú pháp nhất định sẽ không cho phép bạn diễn đạt các thao tác đó nữa, mặc dù ngôn ngữ vẫn có thể hoàn thành Turing.
Giorgio

13

Thuật ngữ đường cú pháp thường dùng để chỉ các trường hợp tính năng được xác định bằng cách thay thế. Ngôn ngữ không định nghĩa những gì một tính năng làm, thay vào đó nó xác định rằng nó hoàn toàn tương đương với một tính năng khác. Vì vậy, ví dụ, cho mỗi vòng lặp

for(Object alpha: alphas) {
}

Trở thành:

for(Iterator<Object> iter = alpha.iterator(); iter.hasNext()) {
   alpha = iter.next();
}

Hoặc lấy một hàm với các đối số biến:

void foo(int... args);

foo(3, 4, 5);

Trở thành:

void Foo(int[] args);

foo(new int[]{3, 4, 5});

Vì vậy, có một sự thay thế tầm thường của cú pháp để thực hiện tính năng này theo các tính năng khác.

Hãy xem xét quá tải phương thức.

void foo(int a);
void foo(double b);

foo(4.5);

Điều này có thể được viết lại như sau:

void foo_int(int a);
void foo_double(double b);

foo_double(4.5);

Nhưng nó không tương đương với điều đó. Trong mô hình của Java, đây là một cái gì đó khác biệt. foo(int a)không thực hiện một foo_intchức năng sẽ được tạo ra. Java không thực hiện nạp chồng phương thức bằng cách đặt các hàm mơ hồ. Để được tính là đường cú pháp, java sẽ phải giả vờ rằng bạn thực sự đã viết foo_intfoo_doublehoạt động nhưng không.


2
Tôi không nghĩ bất cứ ai từng nói việc chuyển đổi cho cú pháp đường phải tầm thường. Ngay cả khi nó đã xảy ra, tôi thấy yêu cầu But, the transformation isn't trivial. At the least, you have to determine the types of the parameters.rất sơ sài vì các loại không cần phải xác định ; chúng được biết đến vào thời gian biên dịch.
Doval

3
"Để được tính là đường cú pháp, java sẽ phải giả vờ rằng bạn thực sự đã viết các hàm foo_int và foo_double nhưng không được." - miễn là chúng ta nói về các phương thức quá tải và không đa hình, sự khác biệt giữa foo(int)/ foo(double)foo_int/ là foo_doublegì? Tôi thực sự không biết rõ về Java nhưng tôi sẽ tưởng tượng rằng việc đổi tên như vậy thực sự xảy ra trong JVM (có lẽ là sử dụng foo(args)thay vào đó foo_args- ít nhất là trong C ++ với tính năng xáo trộn biểu tượng (ok - biểu tượng xáo trộn về mặt kỹ thuật là một chi tiết triển khai và không phải là một phần của ngôn ngữ).
Maciej Piechotka

2
@Doval: "Tôi không nghĩ có ai từng nói việc chuyển đổi cho cú pháp đường phải là chuyện nhỏ." - Đúng, nhưng nó phải là địa phương . Định nghĩa hữu ích duy nhất về đường cú pháp mà tôi biết là từ bài báo nổi tiếng của Matthias Felleisen về biểu cảm ngôn ngữ, và về cơ bản nó nói rằng nếu bạn có thể viết lại một chương trình viết bằng ngôn ngữ L + y (tức là một số ngôn ngữ L có tính năng y ) ngôn ngữ L (nghĩa là một tập hợp con của ngôn ngữ đó không có tính năng y ) mà không thay đổi cấu trúc toàn cầu của chương trình (nghĩa là chỉ thực hiện thay đổi cục bộ), sau đó y là cú pháp cú pháp trong L + y
Jörg W Mittag

2
Không thể tăng tính biểu cảm của L. Tuy nhiên, nếu bạn không thể làm điều đó, tức là nếu bạn phải thực hiện thay đổi đối với cấu trúc toàn cầu của chương trình của bạn, sau đó nó là không đường cú pháp và thực hiện trên thực tế làm cho L + y biểu cảm hơn L . Ví dụ, Java với forvòng lặp nâng cao không biểu cảm hơn Java mà không có nó. (Nó đẹp hơn, ngắn gọn hơn, dễ đọc hơn và xung quanh tốt hơn, tôi sẽ tranh luận, nhưng không biểu cảm hơn.) Tuy nhiên, tôi không chắc chắn về trường hợp quá tải. Có lẽ tôi sẽ phải đọc lại bài báo để chắc chắn. Ruột của tôi nói rằng đó đường cú pháp, nhưng tôi không chắc.
Jörg W Mittag

2
@MaciejPiechotka, nếu đó là một phần của định nghĩa ngôn ngữ mà các hàm được đổi tên và bạn có thể truy cập hàm dưới các tên đó, tôi nghĩ đó sẽ là đường cú pháp. Nhưng bởi vì nó ẩn như một chi tiết thực hiện, tôi nghĩ rằng nó không đủ điều kiện để trở thành đường cú pháp.
Winston Ewert

8

Cho rằng công việc xáo trộn tên đó, không phải nó có gì hơn đường cú pháp?

Nó cho phép người gọi tưởng tượng anh ta đang gọi chức năng tương tự, khi anh ta không. Nhưng anh ta có thể biết tên thật của tất cả các chức năng của mình. Chỉ khi có thể đạt được tính đa hình bị trì hoãn bằng cách chuyển một biến chưa được gõ vào hàm được gõ và loại được thiết lập để cuộc gọi có thể đi đúng phiên bản theo tên thì đây sẽ là một tính năng ngôn ngữ thực sự.

Thật không may, tôi chưa bao giờ thấy một ngôn ngữ làm điều này. Khi có sự mơ hồ, các trình biên dịch này không giải quyết nó, họ yêu cầu người viết giải quyết nó cho họ.


Tính năng bạn đang tìm kiếm có tên là "Nhiều công văn". Rất nhiều ngôn ngữ hỗ trợ nó bao gồm Haskell, Scala và (kể từ 4.0) C #.
Iain Galloway

Tôi muốn tách các tham số trên các lớp khỏi nạp chồng phương thức thẳng. Trong trường hợp nạp chồng phương thức thẳng, lập trình viên viết tất cả các phiên bản, trình biên dịch chỉ cần biết cách chọn một. Đó chỉ là đường cú pháp, và được giải quyết bằng cách xáo trộn tên đơn giản, thậm chí cho nhiều công văn. --- Với sự hiện diện của các tham số trên các lớp, trình biên dịch tạo mã khi cần thiết và điều đó thay đổi hoàn toàn điều này.
Jon Jay Obermark

2
Tôi nghĩ bạn hiểu lầm. Ví dụ, trong C #, nếu một trong các tham số cho một phương thức dynamicthì độ phân giải quá tải xảy ra trong thời gian chạy, không phải ở thời gian biên dịch . Đó là nhiều công văn là gì và nó không thể được sao chép bằng cách đổi tên các chức năng.
Iain Galloway

Khá mát mẻ. Tôi vẫn có thể kiểm tra loại biến, tuy nhiên, đây vẫn chỉ là một hàm tích hợp được phủ trên đường cú pháp. Đây là một tính năng ngôn ngữ, nhưng chỉ hầu như không có.
Jon Jay Obermark

7

Tùy thuộc vào ngôn ngữ, nó là cú pháp đường hay không.

Ví dụ, trong C ++, bạn có thể thực hiện mọi việc bằng cách sử dụng quá tải và các mẫu không thể thực hiện được nếu không có sự phức tạp (viết thủ công tất cả các cảnh báo của mẫu hoặc thêm nhiều tham số mẫu).

Lưu ý rằng cử động là một hình thức quá tải, tự động giải quyết trên một số thông số (đối với một số ngôn ngữ chỉ là một người đặc biệt, này , nhưng không phải mọi ngôn ngữ đều rất hạn chế), và tôi sẽ không gọi đó là hình thức quá tải đường cú pháp.


Tôi không chắc làm thế nào các câu trả lời khác đang làm tốt hơn nhiều khi về cơ bản không chính xác.
Telastyn

5

Đối với các ngôn ngữ đương đại, đó chỉ là cú pháp cú pháp; theo một cách hoàn toàn không biết ngôn ngữ, nó còn hơn thế nữa.

Câu trả lời trước đây chỉ đơn giản là nó nhiều hơn đường cú pháp, nhưng nếu bạn thấy trong các bình luận, Falco đã đưa ra quan điểm rằng có một phần của câu đố mà các ngôn ngữ đương đại dường như bị thiếu; họ không trộn lẫn quá tải phương thức với xác định động của hàm nào sẽ gọi trong cùng một bước. Điều này sẽ được làm rõ sau.

Đây là lý do tại sao nó nên được nhiều hơn.

Xem xét một ngôn ngữ hỗ trợ cả hai phương thức nạp chồng và các biến chưa được kiểm tra. Bạn có thể có các nguyên mẫu phương thức sau:

bool someFunction(int arg);

bool someFunction(string arg);

Trong một số ngôn ngữ, bạn có thể sẽ bị từ chức để biết tại thời điểm biên dịch mà một trong số các ngôn ngữ này sẽ được gọi bởi một dòng mã nhất định. Nhưng trong một số ngôn ngữ, không phải tất cả các biến đều được nhập (hoặc tất cả chúng đều được nhập ngầm Objecthoặc là bất cứ thứ gì), vì vậy hãy tưởng tượng xây dựng một từ điển có khóa ánh xạ tới các giá trị của các loại khác nhau:

dict roomNumber; // some hotels use numbers, some use letters, and some use
                 // alphanumerical strings.  In some languages, built-in dictionary
                 // types automatically use untyped values for their keys to map to,
                 // so it makes more sense then to allow for both ints and strings in
                 // your code.

Bây giờ, nếu bạn muốn áp dụng someFunctioncho một trong những số phòng đó thì sao? Bạn gọi đây là:

someFunction(roomNumber[someSortOfKey]);

Được someFunction(int)gọi, hay được someFunction(string)gọi? Ở đây bạn thấy một ví dụ trong đó đây không phải là các phương pháp trực giao hoàn toàn, đặc biệt là trong các ngôn ngữ cấp cao hơn. Ngôn ngữ phải tìm ra - trong thời gian chạy - mà một trong số chúng phải gọi, vì vậy nó vẫn phải coi những thứ này ít nhất là cùng một phương thức.

Tại sao không chỉ đơn giản là sử dụng các mẫu? Tại sao không chỉ đơn giản là sử dụng một đối số chưa được kiểm tra?

Linh hoạt và kiểm soát hạt mịn hơn. Đôi khi, sử dụng các mẫu / đối số chưa được kiểm tra là một cách tiếp cận tốt hơn, nhưng đôi khi chúng không như vậy.

Bạn phải suy nghĩ về các trường hợp, ví dụ, bạn có thể có hai chữ ký phương thức, mỗi chữ ký lấy một intvà một stringđối số, nhưng trong đó thứ tự là khác nhau trong mỗi chữ ký. Bạn rất có thể có một lý do chính đáng để làm điều này, vì việc thực hiện mỗi chữ ký có thể làm phần lớn giống nhau, nhưng chỉ với một sự thay đổi hơi khác nhau; việc đăng nhập có thể khác nhau, ví dụ. Hoặc thậm chí nếu họ làm điều tương tự chính xác, bạn có thể tự động thu thập thông tin nhất định từ chỉ thứ tự các đối số được chỉ định. Về mặt kỹ thuật, bạn chỉ có thể sử dụng các câu lệnh chuyển đổi giả để xác định loại của từng đối số được truyền vào, nhưng điều đó trở nên lộn xộn.

Vì vậy, đây là ví dụ tiếp theo thực hành lập trình xấu?

bool stringIsTrue(int arg)
{
    if (arg.toString() == "0")
    {
        return false;
    }
    else
    {
        return true;
    }
}

bool stringIsTrue(Object arg)
{
    if (arg.toString() == "0")
    {
        return false;
    }
    else
    {
        return true;
    }
}

bool stringIsTrue(string arg)
{
    if (arg == "0")
    {
        return false;
    }
    else
    {
        return true;
    }
}

Vâng, bởi và lớn. Trong ví dụ cụ thể này, nó có thể ngăn ai đó cố gắng áp dụng điều này cho một số loại nguyên thủy nhất định và lấy lại hành vi bất ngờ (có thể là một điều tốt); nhưng hãy giả sử tôi viết tắt mã ở trên và trên thực tế, bạn có quá tải cho tất cả các loại nguyên thủy, cũng như cho Objects. Sau đó, đoạn mã tiếp theo này thực sự phù hợp hơn:

bool stringIsTrue(untyped arg)
{
    if (arg.toString() == "0")
    {
        return false;
    }
    else
    {
        return true;
    }
}

Nhưng điều gì sẽ xảy ra nếu bạn chỉ cần sử dụng điều này cho ints và strings, và nếu bạn muốn nó trở về đúng dựa trên các điều kiện đơn giản hơn hoặc phức tạp hơn thì sao? Sau đó, bạn có một lý do tốt để sử dụng quá tải:

bool appearsToBeFirstFloor(int arg)
{
    if (arg.digitAt(0) == 1)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool appearsToBeFirstFloor(string arg)
{
    string firstCharacter = arg.characterAt(0);
    if (firstCharacter.isDigit())
    {
        return appearsToBeFirstFloor(int(firstCharacter));
    }
    else if (firstCharacter.toUpper() == "A")
    {
        return true;
    }
    else
    {
        return false;
    }
}

Nhưng này, tại sao không chỉ cung cấp cho các chức năng đó hai tên khác nhau? Bạn vẫn có cùng số lượng kiểm soát chi tiết, phải không?

Bởi vì, như đã nêu trước đây, một số khách sạn sử dụng số, một số sử dụng chữ cái và một số sử dụng hỗn hợp số và chữ:

appearsToBeFirstFloor(roomNumber[someSortOfKey]);

// will treat ints and strings differently, without you having to write extra code
// every single spot where the function is being called

Đây vẫn không chính xác là cùng một mã chính xác mà tôi sẽ sử dụng trong cuộc sống thực, nhưng nó sẽ minh họa điểm tôi đang làm tốt.

Nhưng ... Đây là lý do tại sao nó không hơn đường cú pháp trong các ngôn ngữ đương đại.

Falco nêu quan điểm trong các ý kiến ​​rằng các ngôn ngữ hiện tại về cơ bản không trộn lẫn quá tải phương thức và lựa chọn chức năng động trong cùng một bước. Cách trước đây tôi hiểu một số ngôn ngữ nhất định để làm việc là bạn có thể quá tải appearsToBeFirstFloortrong ví dụ trên, và sau đó ngôn ngữ sẽ xác định trong thời gian chạy phiên bản của hàm sẽ được gọi, tùy thuộc vào giá trị thời gian chạy của biến không được gõ. Sự nhầm lẫn này một phần xuất phát từ việc làm việc với các loại ngôn ngữ ECMA, như ActionScript 3.0, trong đó bạn có thể dễ dàng chọn ngẫu nhiên hàm nào được gọi trên một dòng mã nhất định khi chạy.

Như bạn có thể biết, ActionScript 3 không hỗ trợ nạp chồng phương thức. Đối với VB.NET, bạn có thể khai báo và đặt các biến mà không chỉ định một kiểu rõ ràng, nhưng khi bạn cố gắng chuyển các biến này thành đối số cho các phương thức bị quá tải, nó vẫn không muốn đọc giá trị thời gian chạy để xác định phương thức nào sẽ gọi; thay vào đó, nó muốn tìm một phương thức với các đối số kiểu Objecthoặc không có kiểu hoặc một cái gì đó tương tự. Vì vậy, ví dụ intso với stringở trên cũng không hoạt động trong ngôn ngữ đó. C ++ có các vấn đề tương tự, vì khi bạn sử dụng một cái gì đó như con trỏ void hoặc một số cơ chế khác như thế, nó vẫn yêu cầu bạn phân tán kiểu thủ công trong thời gian biên dịch.

Vì vậy, như tiêu đề đầu tiên nói ...

Đối với các ngôn ngữ đương đại, đó chỉ là cú pháp cú pháp; theo một cách hoàn toàn không biết ngôn ngữ, nó còn hơn thế nữa. Làm cho phương thức nạp chồng trở nên hữu ích và phù hợp hơn, như trong ví dụ trên, thực sự có thể là một tính năng tốt để thêm vào một ngôn ngữ hiện có (như đã được yêu cầu rộng rãi cho AS3), hoặc nó cũng có thể đóng vai trò là một trong nhiều trụ cột cơ bản khác nhau cho việc tạo ra một ngôn ngữ hướng thủ tục / hướng đối tượng mới.


3
Bạn có thể đặt tên cho bất kỳ ngôn ngữ nào thực sự xử lý Chức năng phân phối trong thời gian chạy và không biên dịch thời gian không? TẤT CẢ các ngôn ngữ tôi biết yêu cầu sự chắc chắn về thời gian biên dịch của hàm được gọi là ...
Falco

@Falco ActionScript 3.0 xử lý nó khi chạy. Bạn có thể, ví dụ, sử dụng một hàm trả về một trong ba chuỗi một cách ngẫu nhiên, và sau đó sử dụng giá trị trả về của nó để gọi bất kỳ một trong ba chức năng một cách ngẫu nhiên: this[chooseFunctionNameAtRandom](); Nếu chooseFunctionNameAtRandom()lợi nhuận trong hai "punch", "kick"hoặc "dodge", sau đó bạn có thể thusly thực hiện rất đơn giản ngẫu nhiên ví dụ, yếu tố AI của kẻ thù trong trò chơi Flash.
Panzercrisis

1
Đúng - nhưng cả hai đều là các phương thức ngữ nghĩa thực sự để có được chức năng động, Java cũng có những phương thức này. Nhưng chúng khác với quá tải, quá tải là tĩnh và chỉ là cú pháp, trong khi công văn động và kế thừa là các tính năng ngôn ngữ thực, cung cấp chức năng mới!
Falco

1
... Tôi cũng đã thử void con trỏ trong C ++, cũng như các con trỏ lớp cơ sở, nhưng trình biên dịch muốn tôi tự định hướng nó trước khi chuyển nó vào một hàm. Vì vậy, bây giờ tôi đang tự hỏi có nên xóa câu trả lời này. Nó bắt đầu giống như các ngôn ngữ hầu như luôn luôn đi đúng để kết hợp lựa chọn chức năng động với quá tải chức năng trong cùng một hướng dẫn hoặc câu lệnh, nhưng sau đó bỏ đi ở giây cuối cùng. Nó sẽ là một tính năng ngôn ngữ tốt mặc dù; có lẽ ai đó cần tạo ra một ngôn ngữ có cái này
Panzercrisis

1
Hãy để câu trả lời ở lại, có thể suy nghĩ về việc bao gồm một số nghiên cứu của bạn từ các ý kiến ​​trong câu trả lời?
Falco

2

Nó thực sự phụ thuộc vào định nghĩa của bạn về "đường cú pháp". Tôi sẽ cố gắng giải quyết một số định nghĩa xuất hiện trong đầu tôi:

  1. Một tính năng là đường cú pháp khi một chương trình sử dụng nó luôn có thể được dịch sang một chương trình khác không sử dụng tính năng này.

    Ở đây, chúng tôi giả định rằng tồn tại một tập hợp các tính năng nguyên thủy không thể dịch được: nói cách khác, không có vòng lặp nào "bạn có thể thay thế tính năng X bằng tính năng Y" và "bạn có thể thay thế tính năng Y bằng tính năng X". Nếu một trong hai tính năng này đúng hơn một trong hai tính năng khác thì có thể được thể hiện bằng các tính năng không phải là tính năng đầu tiên hoặc đó là tính năng nguyên thủy.

  2. Giống như định nghĩa 1 nhưng với yêu cầu bổ sung rằng chương trình dịch là loại an toàn như lần đầu tiên, tức là bằng cách giải mã, bạn không mất bất kỳ loại thông tin nào.

  3. Định nghĩa của OP: một tính năng là đường cú pháp nếu bản dịch của nó không thay đổi cấu trúc của chương trình mà chỉ yêu cầu "thay đổi cục bộ".

Hãy lấy Haskell làm ví dụ cho quá tải. Haskell cung cấp quá tải do người dùng định nghĩa thông qua các lớp loại. Ví dụ, các hoạt động +*được định nghĩa trong Numlớp loại và bất kỳ loại nào có một thể hiện (hoàn thành) của lớp đó có thể được sử dụng với +. Ví dụ:

instance Num a => Num (b, a) where
    (x, y) + (_, y') = (x, y + y')
    -- other definitions

("Hello", 1) + ("World", 3) -- -> ("Hello", 4)

Một điều nổi tiếng về các loại lớp của Haskell là bạn có thể thoát khỏi chúng . Tức là bạn có thể dịch bất kỳ chương trình nào sử dụng các lớp loại trong một chương trình tương đương không sử dụng chúng.

Bản dịch khá đơn giản:

  • Đưa ra một định nghĩa lớp:

    class (P_1 a, ..., P_n a) => X a where
        op_1 :: t_1   ... op_m :: t_m
    

    Bạn có thể dịch nó thành một kiểu dữ liệu đại số:

    data X a = X {
        X_P_1 :: P_1 a, ... X_P_n :: P_n a,
        X_op_1 :: t_1, ..., X_op_m :: t_m
    }
    

    Đây X_P_iX_op_ilà những người lựa chọn . Tức là đã cho một giá trị của kiểu X aáp dụng X_P_1cho giá trị sẽ trả về giá trị được lưu trữ trong trường đó, vì vậy chúng là các hàm có kiểu X a -> P_i a(hoặc X a -> t_i).

    Đối với một anology rất thô, bạn có thể nghĩ về các giá trị cho kiểu X astructs và sau đó nếu xlà kiểu X abiểu thức:

    X_P_1 x
    X_op_1 x
    

    có thể được xem là:

    x.X_P_1
    x.X_op_1
    

    (Thật dễ dàng để chỉ sử dụng các trường vị trí thay vì các trường được đặt tên, nhưng các trường được đặt tên sẽ dễ xử lý hơn trong các ví dụ và tránh một số mã của nồi hơi).

  • Đưa ra một tuyên bố ví dụ:

    instance (C_1 a_1, ..., C_n a_n) => X (T a_1 ... a_n) where
        op_1 = ...; ...;  op_m = ...
    

    Bạn có thể dịch nó thành một hàm đưa ra các từ điển cho các C_1 a_1, ..., C_n a_nlớp trả về một giá trị từ điển (tức là một giá trị của loại X a) cho loại T a_1 ... a_n.

    Nói cách khác, ví dụ trên có thể được dịch sang một hàm như:

    f :: C_1 a_1 -> ... -> C_n a_n -> X (T a_1 ... a_n)
    

    (Lưu ý rằng ncó thể 0).

    Và trên thực tế chúng ta có thể định nghĩa nó là:

    f c1 ... cN = X {X_P_1=get_P_1_T, X_P_n=get_P_n_T,
                     X_op_1=op_1, ..., X_op_m=op_m}
        where
            op_1 = ...
            ...
            op_m = ...
    

    nơi op_1 = ...để op_m = ...là các định nghĩa được tìm thấy trong các instancetuyên bố và get_P_i_Tlà chức năng được xác định bởi các P_ithể hiện của các Tloại (những phải tồn tại vì P_is là superclasses của X).

  • Đưa ra một cuộc gọi đến một chức năng quá tải:

    add :: Num a => a -> a -> a
    add x y = x + y
    

    Chúng ta có thể chuyển các từ điển liên quan đến các ràng buộc của lớp một cách rõ ràng và có được một cuộc gọi tương đương:

    add :: Num a -> a -> a -> a
    add dictNum x y = ((+) dictNum) x y
    

    Lưu ý cách các ràng buộc lớp đơn giản trở thành một đối số mới. Các +chương trình dịch là selector như đã giải thích trước đó. Nói cách khác add, hàm được dịch , được đưa ra từ điển cho loại đối số của nó trước tiên sẽ "giải nén" hàm thực tế để tính kết quả bằng cách sử dụng (+) dictNumvà sau đó sẽ áp dụng hàm này cho các đối số.

Đây chỉ là một bản phác thảo rất nhanh về toàn bộ. Nếu bạn quan tâm, bạn nên đọc các bài viết của Simon Peyton Jones et al.

Tôi tin rằng một cách tiếp cận tương tự cũng có thể được sử dụng để quá tải trong các ngôn ngữ khác.

Tuy nhiên, điều này cho thấy rằng, nếu định nghĩa của bạn về đường cú pháp là (1), thì quá tải là đường cú pháp . Bởi vì bạn có thể thoát khỏi nó.

Tuy nhiên, chương trình dịch mất một số thông tin về chương trình gốc. Ví dụ, nó không thực thi rằng các thể hiện cho các lớp cha tồn tại. (Mặc dù các thao tác trích xuất từ ​​điển của cha mẹ vẫn phải thuộc loại đó, bạn có thể chuyển vào undefinedhoặc các giá trị đa hình khác để bạn có thể tạo giá trị X ymà không cần xây dựng các giá trị cho P_i y, vì vậy bản dịch không mất hết Các loại an toàn). Do đó, nó không phải là đường tổng hợp theo (2)

Đối với (3). Tôi không biết câu trả lời nên là có hay không.

Tôi muốn nói không bởi vì, ví dụ, một khai báo thể hiện trở thành một định nghĩa hàm. Các hàm bị quá tải có được một tham số mới (có nghĩa là nó thay đổi cả định nghĩa và tất cả các cuộc gọi).

Tôi muốn nói có bởi vì hai chương trình vẫn ánh xạ một-một, vì vậy "cấu trúc" thực sự không thay đổi nhiều.


Điều này nói rằng, tôi muốn nói rằng những lợi thế thực dụng được đưa ra bởi quá tải là rất lớn đến nỗi sử dụng thuật ngữ "xúc phạm" như "đường cú pháp" có vẻ không đúng.

Bạn có thể dịch tất cả cú pháp Haskell sang ngôn ngữ Core rất đơn giản (thực sự được thực hiện khi biên dịch), vì vậy hầu hết cú pháp Haskell có thể được xem là "đường cú pháp" cho một thứ chỉ là lambda-compus cộng với một chút cấu trúc mới. Tuy nhiên, chúng tôi có thể đồng ý rằng các chương trình Haskell dễ xử lý hơn và rất súc tích, trong khi các chương trình dịch khá khó đọc hoặc suy nghĩ.


2

Nếu công văn được giải quyết tại thời điểm biên dịch, chỉ phụ thuộc vào loại tĩnh của biểu thức đối số, thì bạn chắc chắn có thể lập luận rằng đó là "đường cú pháp" thay thế hai phương thức khác nhau bằng các tên khác nhau, miễn là lập trình viên "biết" loại tĩnh và chỉ có thể sử dụng đúng tên phương thức thay cho tên quá tải. Nó cũng là một dạng đa hình tĩnh, nhưng ở dạng hạn chế đó thường không mạnh lắm.

Tất nhiên sẽ rất phiền toái khi phải thay đổi tên của các phương thức bạn gọi bất cứ khi nào bạn thay đổi loại biến, nhưng ví dụ trong ngôn ngữ C, nó được coi là phiền toái có thể quản lý được, vì vậy C không bị quá tải chức năng (mặc dù bây giờ nó có macro chung).

Trong các mẫu C ++ và trong bất kỳ ngôn ngữ nào không loại trừ loại tĩnh không tầm thường, bạn thực sự không thể tranh luận rằng đây là "đường tổng hợp" trừ khi bạn cũng cho rằng khấu trừ loại tĩnh là "đường cú pháp". Sẽ không có phiền toái nếu không có các mẫu và trong ngữ cảnh của C ++, đó sẽ là một "phiền toái không thể kiểm soát", vì chúng rất đặc trưng đối với ngôn ngữ và các thư viện chuẩn của nó. Vì vậy, trong C ++, nó không chỉ là một người trợ giúp tốt, nó quan trọng đối với phong cách ngôn ngữ, và vì vậy tôi nghĩ bạn phải gọi nó nhiều hơn là "đường cú pháp".

Trong Java, bạn có thể xem xét nó nhiều hơn là một sự thuận tiện khi xem xét ví dụ có bao nhiêu quá tải PrintStream.printPrintStream.println. Nhưng sau đó, có nhiều DataInputStream.readXphương thức vì Java không quá tải đối với kiểu trả về, vì vậy trong một số trường hợp, nó chỉ là để thuận tiện. Đó là tất cả cho các loại nguyên thủy.

Tôi không nhớ những gì xảy ra trong Java nếu tôi có các lớp học ABmở rộng O, tôi quá tải phương pháp foo(O), foo(A)foo(B), và sau đó trong một chung với <T extends O>tôi gọi foo(t)nơi tlà một thể hiện của T. Trong trường hợp TAđể tôi có công văn dựa trên quá tải hoặc là nó như thể tôi gọi foo(O)?

Nếu trước đây, thì quá tải phương thức Java tốt hơn đường theo cách tương tự như quá tải C ++. Sử dụng định nghĩa của bạn, tôi cho rằng trong Java tôi có thể viết cục bộ một loạt các kiểm tra loại (sẽ rất mong manh, vì quá tải mới foosẽ yêu cầu kiểm tra bổ sung). Ngoài việc chấp nhận sự mong manh đó, tôi không thể thực hiện thay đổi cục bộ tại trang web cuộc gọi để làm cho đúng, thay vào đó tôi phải từ bỏ việc viết mã chung. Tôi cho rằng việc ngăn chặn mã cồng kềnh có thể là đường cú pháp, nhưng ngăn chặn mã dễ vỡ còn hơn thế. Vì lý do đó, đa hình tĩnh nói chung không chỉ là đường cú pháp. Tình huống trong một ngôn ngữ cụ thể có thể khác nhau, tùy thuộc vào mức độ ngôn ngữ cho phép bạn có được bằng cách "không biết" loại tĩnh.


Trong Java, tình trạng quá tải được giải quyết tại thời điểm biên dịch. Với việc sử dụng loại tẩy, chúng sẽ không thể khác được. Hơn nữa, ngay cả khi không loại tẩy xoá, nếu T:Animallà được loại SiameseCatvà quá tải hiện có Cat Foo(Animal), SiameseCat Foo(Cat)Animal Foo(SiameseCat), trong đó tình trạng quá tải nên được lựa chọn nếu TSiameseCat?
supercat

@supercat: có ý nghĩa Vì vậy, tôi có thể đã tìm ra câu trả lời mà không cần nhớ (hoặc, tất nhiên, chạy nó). Do đó, quá tải Java không tốt hơn đường theo cách tương tự như quá tải C ++ liên quan đến mã chung. Vẫn có thể có một số cách khác trong đó chúng tốt hơn là chỉ là một chuyển đổi cục bộ. Tôi tự hỏi liệu tôi có nên thay đổi ví dụ của mình thành C ++ hay để nó dưới dạng một số Java tưởng tượng-đó-không-có-thật-Java.
Steve Jessop

Quá tải có thể hữu ích trong trường hợp các phương thức có đối số tùy chọn, nhưng chúng cũng có thể nguy hiểm. Giả sử dòng long foo=Math.round(bar*1.0001)*5được thay đổi thành long foo=Math.round(bar)*5. Điều đó sẽ ảnh hưởng đến ngữ nghĩa như thế nào nếu barbằng, ví dụ, 123456789L?
supercat

@supercat Tôi cho rằng mối nguy hiểm thực sự có sự chuyển đổi ngầm định từ longsang double.
Doval

@Doval: Đến double?
supercat

1

Có vẻ như "đường cú pháp" nghe có vẻ xúc phạm, như vô dụng hoặc phù phiếm. Đó là lý do tại sao câu hỏi kích hoạt nhiều câu trả lời tiêu cực.

Nhưng bạn đã đúng, quá tải phương thức không thêm bất kỳ tính năng nào vào ngôn ngữ ngoại trừ khả năng sử dụng cùng tên cho các phương thức khác nhau. Bạn có thể làm cho loại tham số rõ ràng, chương trình vẫn sẽ hoạt động như nhau.

Điều tương tự áp dụng cho tên gói. Chuỗi chỉ là cú pháp đường cho java.lang.String.

Trong thực tế, một phương pháp như

void fun(int i, String c);

trong lớp MyClass nên được gọi là "my_package_MyClass_fun_int_java_lang_String". Điều này sẽ xác định phương pháp duy nhất. (JVM làm một cái gì đó như thế trong nội bộ). Nhưng bạn không muốn viết điều đó. Đó là lý do tại sao trình biên dịch sẽ cho phép bạn viết vui vẻ (1, "một") và xác định đó là phương thức nào.

Tuy nhiên, có một điều bạn có thể làm với quá tải: Nếu bạn nạp chồng một phương thức có cùng số lượng đối số, trình biên dịch sẽ tự động tìm ra phiên bản nào phù hợp nhất với đối số được đưa ra bởi các đối số phù hợp, không chỉ với các loại bằng nhau, mà còn ở đâu đối số đã cho là một lớp con của đối số khai báo.

Nếu bạn có hai thủ tục quá tải

addParameter(String name, Object value);
addParameter(String name, Date value);

bạn không cần biết rằng có một phiên bản cụ thể của quy trình dành cho Ngày. addParameter ("hello", "world) sẽ gọi phiên bản đầu tiên, addParameter (" now ", Date mới ()) sẽ gọi phiên bản thứ hai.

Tất nhiên, bạn nên tránh làm quá tải một phương thức với một phương thức khác thực hiện một điều hoàn toàn khác.


1

Thật thú vị, câu trả lời cho câu hỏi này sẽ phụ thuộc vào ngôn ngữ.

Cụ thể, có một sự tương tác giữa quá tảilập trình chung (*), và tùy thuộc vào cách thực hiện lập trình chung, nó có thể chỉ là đường cú pháp (Rust) hoặc hoàn toàn cần thiết (C ++).

Đó là, khi lập trình chung được triển khai với các giao diện rõ ràng (trong Rust hoặc Haskell, đó sẽ là các lớp loại), thì quá tải chỉ là đường cú pháp; hoặc thực sự có thể không phải là một phần của ngôn ngữ.

Mặt khác, khi lập trình chung được thực hiện bằng cách gõ vịt (có thể là động hoặc tĩnh) thì tên của phương thức là một hợp đồng thiết yếu và do đó quá tải là bắt buộc để hệ thống hoạt động.

(*) Được sử dụng theo nghĩa là viết một phương thức một lần, để vận hành trên nhiều loại khác nhau theo kiểu đồng nhất.


0

Trong một số ngôn ngữ, không nghi ngờ gì chỉ là cú pháp đường. Tuy nhiên, những gì nó là một đường phụ thuộc vào quan điểm của bạn. Tôi sẽ để lại cuộc thảo luận này để sau này trong câu trả lời này.

Bây giờ tôi chỉ muốn lưu ý rằng trong một số ngôn ngữ, nó chắc chắn không phải là cú pháp. Ít nhất không phải không yêu cầu bạn sử dụng một logic / thuật toán hoàn toàn khác để thực hiện điều tương tự. Nó giống như tuyên bố đệ quy là đường cú pháp (đó là vì bạn có thể viết tất cả thuật toán đệ quy với một vòng lặp và một ngăn xếp).

Một ví dụ về việc sử dụng rất khó thay thế đến từ một ngôn ngữ mà trớ trêu thay, không gọi tính năng này là "quá tải chức năng". Thay vào đó, nó được gọi là "khớp mẫu" (có thể được xem như là một siêu tải vì chúng ta có thể quá tải không chỉ các loại mà cả các giá trị).

Đây là cách triển khai ngây thơ cổ điển của hàm Fibonacci trong Haskell:

fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

Có thể thay thế ba chức năng bằng một if / other vì nó thường được thực hiện trong bất kỳ ngôn ngữ nào khác. Nhưng điều đó về cơ bản làm cho định nghĩa hoàn toàn đơn giản:

fib n = fib (n-1) + fib (n-2)

lộn xộn hơn nhiều và không thể hiện trực tiếp khái niệm toán học của chuỗi Fibonacci.

Vì vậy, đôi khi nó có thể là cú pháp đường nếu việc sử dụng duy nhất là cho phép bạn gọi một hàm với các đối số khác nhau. Nhưng đôi khi nó còn cơ bản hơn thế nhiều.


Bây giờ cho các đĩa của những gì quá tải toán tử có thể là một đường cho. Bạn đã xác định một trường hợp sử dụng - nó có thể được sử dụng để triển khai các hàm tương tự có các đối số khác nhau. Vì thế:

function print (string x) { stdout.print(x) };
function print (number x) { stdout.print(x.toString) };

cách khác có thể được thực hiện như:

function printString (string x) {...}
function printNumber (number x) {...}

hoặc thậm chí:

function print (auto x) {
    if (x instanceof String) {...}
    if (x instanceof Number) {...}
}

Nhưng quá tải toán tử cũng có thể là một đường để thực hiện các đối số tùy chọn (một số ngôn ngữ có quá tải toán tử nhưng không phải là đối số tùy chọn):

function print (string x) {...}
function print (string x, stream io) {...}

có thể được sử dụng để thực hiện:

function print (string x, stream io=stdout) {...}

Trong một ngôn ngữ như vậy (google "Ngôn ngữ Ferite") loại bỏ toán tử quá tải loại bỏ mạnh mẽ một tính năng - đối số tùy chọn. Được cấp bằng các ngôn ngữ có cả hai tính năng (c ++) loại bỏ cái này hoặc cái kia sẽ không có hiệu lực ròng vì có thể được sử dụng để thực hiện các đối số tùy chọn.


Haskell là một ví dụ điển hình về lý do tại sao quá tải toán tử không phải là đường cú pháp, nhưng tôi nghĩ một ví dụ tốt hơn sẽ giải mã một kiểu dữ liệu đại số với khớp mẫu (một điều mà theo như tôi biết là không thể nếu không khớp mẫu).
11684

@ 11684: Bạn có thể chỉ ra một ví dụ không? Tôi thực sự không biết Haskell chút nào nhưng thấy mô hình của nó phù hợp cực kỳ thanh lịch khi tôi thấy ví dụ cụ thể đó (trên computerphile trên youtube).
slebetman

Đưa ra một kiểu dữ liệu như data PaymentInfo = CashOnDelivery | Adress String | UserInvoice CustomerInfobạn có thể khớp mẫu trên các hàm tạo kiểu.
11684

Như thế này : getPayment :: PaymentInfo -> a getPayment CashOnDelivery = error "Should have been paid already" getPayment (Adress addr) = -- code to notify administration to send a bill getPayment (UserInvoice cust) = --whatever. I took the data type from a Haskell tutorial and have no idea what an invoice is. Tôi hy vọng nhận xét này là một phần dễ hiểu.
11684

0

Tôi nghĩ rằng đó là đường cú pháp đơn giản trong hầu hết các ngôn ngữ (ít nhất là tất cả những gì tôi biết ...) vì tất cả chúng đều yêu cầu một hàm gọi rõ ràng vào thời gian biên dịch. Và trình biên dịch chỉ cần thay thế lời gọi hàm bằng một con trỏ rõ ràng để chữ ký thực hiện bên phải.

Ví dụ trong Java:

String s; int i;
mangle(s);  // Translates to CALL ('mangle':LString):(s)
mangle(i);  // Translates to CALL ('mangle':Lint):(i)

Vì vậy, cuối cùng, nó có thể được thay thế hoàn toàn bằng một macro trình biên dịch đơn giản bằng tìm kiếm và thay thế, thay thế hàm Mangle bị quá tải bằng mangle_String và mangle_int - vì danh sách đối số là một phần của định danh hàm cuối cùng, đây thực tế là những gì xảy ra -> và do đó nó chỉ là cú pháp đường.

Bây giờ nếu có một ngôn ngữ, trong đó hàm thực sự được xác định trong thời gian chạy, như với các Phương thức được ghi đè trong các đối tượng, thì điều này sẽ khác. Nhưng tôi không nghĩ có một ngôn ngữ như vậy, vì phương thức tải quá mức có xu hướng mơ hồ, mà trình biên dịch không thể giải quyết và phải được lập trình viên xử lý với một diễn viên rõ ràng. Điều này không thể được thực hiện trong thời gian chạy.


0

Trong loại thông tin Java được biên dịch trong và quá tải nào được gọi là quyết định tại thời điểm biên dịch.

Sau đây là đoạn trích từ sun.misc.Unsafe(tiện ích dành cho Nguyên tử) như được xem trong trình soạn thảo tệp lớp của Eclipse.

  // Method descriptor #143 (Ljava/lang/Object;I)I (deprecated)
  // Stack: 4, Locals: 3
  @java.lang.Deprecated
  public int getInt(java.lang.Object arg0, int arg1);
    0  aload_0 [this]
    1  aload_1 [arg0]
    2  iload_2 [arg1]
    3  i2l
    4  invokevirtual sun.misc.Unsafe.getInt(java.lang.Object, long) : int [231]
    7  ireturn
      Line numbers:
        [pc: 0, line: 213]

như bạn có thể thấy thông tin loại của phương thức được gọi (dòng 4) được bao gồm trong cuộc gọi.

Điều này có nghĩa là bạn có thể tạo một trình biên dịch java lấy thông tin kiểu. Ví dụ: sử dụng ký hiệu như vậy, nguồn trên sẽ là:

@Deprecated
public in getInt(Object arg0, int arg1){
     return getInt$(Object,long)(arg0, arg1);
}

và dàn diễn viên dài sẽ là tùy chọn.

Trong các ngôn ngữ được biên dịch nhập tĩnh khác, bạn sẽ thấy một thiết lập tương tự trong đó trình biên dịch sẽ quyết định mức quá tải nào sẽ được gọi tùy thuộc vào loại và đưa nó vào liên kết / cuộc gọi.

Ngoại lệ là các thư viện động C trong đó không bao gồm thông tin loại và cố gắng tạo một hàm quá tải sẽ khiến trình liên kết phàn nàn.

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.