Quảng cáo kiểu Java trong các tham số


20

Tôi tình cờ thấy đoạn trích này:

public class ParamTest {
    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Điều này sẽ dẫn đến một lỗi biên dịch:

Lỗi: (15, 9) java: tham chiếu đến printSum không rõ ràng cả phương thức printSum (int, double) trong ParamTest và phương thức printSum (dài, dài) trong trận đấu ParamTest

Làm thế nào là mơ hồ? Không nên chỉ tham số thứ hai trong trường hợp này vì tham số đầu tiên đã là int? Các param đầu tiên không cần phải được thúc đẩy trong trường hợp này phải không?

Quá trình biên dịch thành công nếu tôi cập nhật mã để thêm phương thức khác:

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

Hãy để tôi mở rộng chỉ để làm rõ. Mã dưới đây dẫn đến sự mơ hồ:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Sau đó, mã này dưới đây cũng dẫn đến sự mơ hồ:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Tuy nhiên, điều này không dẫn đến sự mơ hồ:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, double b) {
        System.out.println("In longDBL " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

2
Trình biên dịch có thể ánh xạ printSum cuộc gọi của bạn (1, 2); thành printSum (dài a, dài b) hoặc printSum (int a, double b) do đó không rõ ràng. Bạn phải rõ ràng giúp trình biên dịch lựa chọn trong số đó bằng cách đưa ra loại chính xác như thế này: printSum (1, 2d)
Ravindra Ranwala

6
Bạn đã trích dẫn sai thông báo lỗi và sự khác biệt là rất quan trọng. Thông báo lỗi thực tế là: Error:(15, 9) java: reference to printSum is ambiguous both method printSum(int,double) in ParamTest and method printSum(long,long) in ParamTest match- đó không phải là phương thức mơ hồ, đó là lời gọi đến phương thức không rõ ràng.
Erwin Bolwidt

1
@ErwinBolwidt thông báo lỗi là từ nhật thực xin lỗi tôi đã trích dẫn sai ở đó. Dù sao, tôi vẫn không hiểu vì thêm printSum (int a, b) sẽ xóa thông báo lỗi.
riruzen

2
JLS-5.3 => Nếu loại biểu thức không thể được chuyển đổi thành loại tham số bằng một chuyển đổi được phép trong ngữ cảnh gọi lỏng lẻo, thì sẽ xảy ra lỗi thời gian biên dịch. dường như được áp dụng cho bối cảnh, nhưng không thực sự đơn giản để suy luận làm thế nào. +1
Naman

1
Chúng tôi chắc chắn cần một câu hỏi kinh điển cho vấn đề này: stackoverflow.com/ . "
Marco13

Câu trả lời:


17

Tôi nghĩ rằng điều này có liên quan đến quy tắc cụ thể của JLS về 15.12.2.5. Chọn phương pháp cụ thể nhất . Nó nói rằng:

Nếu có nhiều hơn một phương thức thành viên có thể truy cập và áp dụng được cho một lời gọi phương thức, thì cần phải chọn một phương thức để cung cấp bộ mô tả cho công văn phương thức thời gian chạy. Ngôn ngữ lập trình Java sử dụng quy tắc mà phương thức cụ thể nhất được chọn.

Cách Java chọn phương thức cụ thể nhất được giải thích thêm bằng văn bản:

Trực giác không chính thức là một phương thức cụ thể hơn phương thức khác nếu bất kỳ lệnh gọi nào được xử lý bởi phương thức đầu tiên có thể được chuyển sang phương thức khác mà không có lỗi thời gian biên dịch. Trong các trường hợp như một đối số biểu thức lambda được gõ rõ ràng (§15.27.1) hoặc một lời gọi arity biến đổi (§15.12.2.4), một số tính linh hoạt được phép điều chỉnh một chữ ký này sang chữ ký kia.

Trong trường hợp ví dụ của bạn, tất cả các phương thức đều có thể truy cập và áp dụng cho việc gọi phương thức, do đó, Java cần xác định phương thức nào là cụ thể nhất .

Đối với các phương pháp này, không phương pháp nào có thể được xác định là cụ thể hơn:

public static void printSum(int a, double b) {
    System.out.println("In intDBL " + (a + b));
} // int, double cannot be passed to long, long or double, long without error

public static void printSum(long a, long b) {
    System.out.println("In long " + (a + b));
} // long , long cannot be passed to int, double or double, long without error

public static void printSum(double a, long b) {
    System.out.println("In doubleLONG " + (a + b));
} // double, long cannot be passed to int, double or long, long without error

Phương pháp thứ tư xóa chính xác sự mơ hồ bởi vì nó đáp ứng các điều kiện cần thiết để được cụ thể nhất .

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

Nghĩa là, (int, long) có thể được truyền cho (int, double), (dài, dài) hoặc (double, long) mà không có lỗi biên dịch.


2
Vì vậy, để mùa hè, nếu tất cả các phương thức có thể truy cập được bằng cuộc gọi, thì java xác định phương thức đó, có thể gọi đến các phương thức khác mà không cần biên dịch lỗi thời gian, nếu tìm thấy thì sử dụng như là cụ thể nhất và gọi nó là lỗi mơ hồ? Tôi nghĩ rằng, đây là câu trả lời hợp lệ @riruzen.
Sandeep Kokate

Cảm ơn! Tôi đã thử điều này với (double, int) vs (double, long) và nó thực sự đã xóa tan sự mơ hồ, sau đó (double, int) vs (long, double) mơ hồ như mong đợi.
riruzen

7

Nó thực sự là một câu hỏi rất thú vị. Chúng ta hãy đi qua từng bước Đặc tả ngôn ngữ Java.

  1. Khi trình biên dịch đang cố gắng xác định các phương thức có khả năng áp dụng, điều đầu tiên nó làm là tìm kiếm các phương thức được áp dụng bởi Strict Invocation .

  2. Trong trường hợp của bạn không có phương pháp nào như vậy, vì vậy bước tiếp theo là tìm phương thức áp dụng bằng Loose Invocation

  3. Tại thời điểm này, tất cả các phương thức đều khớp, vì vậy phương thức cụ thể nhất ( §15.12.2.5 ) được chọn trong số các phương thức được áp dụng bằng cách gọi lỏng lẻo.

Đây là một thời điểm quan trọng, vì vậy hãy xem xét kỹ điều này.

Một phương thức áp dụng m1 cụ thể hơn một phương thức áp dụng khác m2, đối với một lời gọi với các biểu thức đối số e1, ..., ek, nếu bất kỳ điều nào sau đây là đúng:

(Chúng tôi chỉ quan tâm đến trường hợp sau):

  • m2 không phải là chung và m1 và m2 được áp dụng bằng cách gọi nghiêm ngặt hoặc lỏng lẻo và trong đó m1 có các loại tham số chính thức S1, ..., Sn và m2 có các loại tham số chính thức T1, ..., Tn, loại Si là nhiều hơn cụ thể hơn Ti cho đối số ei cho tất cả i (1 i ≤ n, n = k).

Nói một cách đơn giản, một phương thức cụ thể hơn nếu tất cả các loại tham số của nó cụ thể hơn . Và

Loại S cụ thể hơn loại T cho bất kỳ biểu thức nào nếu S <: T ( §4.10 ).

Biểu hiện S <: Tcó nghĩa Slà một kiểu con của T. Đối với người nguyên thủy, chúng ta có mối quan hệ sau:

double > float > long > int

Vì vậy, hãy xem xét các phương pháp của bạn và xem phương pháp nào cụ thể hơn các phương pháp khác.

public static void printSum(int a, double b) {  // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(double a, long b) { // method 2
    System.out.println("In doubleLONG " + (a + b));
}

Trong ví dụ này, tham số đầu tiên của phương thức 1 rõ ràng cụ thể hơn tham số đầu tiên của phương thức 2 (nếu bạn gọi chúng bằng các giá trị nguyên printSum(1, 2):). Nhưng tham số thứ hai cụ thể hơn cho phương thức 2 , bởi vì long < double. Vì vậy, không có phương pháp nào là cụ thể hơn phương pháp khác. Đó là lý do tại sao bạn có một sự mơ hồ ở đây.

Trong ví dụ sau:

public static void printSum(int a, double b) { // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(long a, double b) { // method 2
    System.out.println("In longDBL " + (a + b));
}

kiểu tham số đầu tiên của phương thức 1 cụ thể hơn kiểu tham số trong phương thức 2, bởi vì int < longkiểu tham số thứ hai giống nhau cho cả hai, đó là lý do tại sao phương thức 1 được chọn.


Tôi không đánh giá thấp câu trả lời của bạn, nhưng câu trả lời này không giải thích được tại sao (int, double) lại mơ hồ với (dài, dài) vì rõ ràng, (int, double) nên cụ thể hơn đối với tham số đầu tiên.
riruzen

3
@riruzen nó giải thích: doublekhông cụ thể hơn long. Và đối với phương thức được chọn, tất cả các tham số loại phải cụ thể hơn: loại Si cụ thể hơn Ti cho đối số ei cho tất cả i (1 ≤ i ≤ n, n = k)
Kirill Simonov

Nó sẽ tăng +1
Trà

0

bởi vì giá trị int cũng có thể được coi là gấp đôi trong java. có nghĩa double a = 3là hợp lệ và tương tự với lâu dài long b = 3Vì vậy, đó là lý do tại sao nó đang tạo ra một sự mơ hồ. Bạn gọi

printSum(1, 2);

Khó hiểu cho cả ba phương thức, bởi vì cả ba phương pháp này đều hợp lệ:

int a = 1;
double b =1;
long c = 1;

Bạn có thể đặt L ở cuối, để xác định rằng đó là giá trị dài. ví dụ:

printSum(1L, 2L);

cho gấp đôi bạn cần chuyển đổi nó:

printSum((double)1, 2L);

cũng đọc bình luận của @Erwin Bolwidt


Có trình biên dịch nhầm lẫn cho cả 3 phương thức, trình biên dịch đầu tiên tìm loại chính xác và nếu nó không tìm thấy loại nào thì hãy thử tìm loại tương thích, và trong trường hợp này tất cả đều tương thích.
Một lập trình viên khác

2
Nếu tôi có 4 khai báo phương thức thay vì 3, sự mơ hồ sẽ tắt: public static void printSum (int a, double b) public static void printSum (int a, long b) public static void printSum (long a, long b) public static void printSum (double a, long b) vì vậy trong khi tôi đồng ý với câu trả lời của bạn, nó vẫn không trả lời câu hỏi. Java thực hiện quảng cáo kiểu tự động nếu loại chính xác không có sẵn nếu tôi không nhầm. Tôi chỉ không hiểu tại sao nó trở nên mơ hồ với những người đó 3.
riruzen
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.