Promosi tipe Java dalam parameter

20

Saya menemukan potongan ini:

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);
    }
}

Ini akan menghasilkan kesalahan kompilasi:

Kesalahan: (15, 9) java: referensi ke printSum bersifat mendua baik metode printSum (int, dobel) di ParamTest dan metode printSum (panjang, panjang) di pertandingan ParamTest

Bagaimana ini ambigu? Bukankah hanya parameter kedua yang dipromosikan dalam kasus ini karena parameter pertama sudah menjadi int? Param pertama tidak perlu dipromosikan dalam kasus ini, kan?

Kompilasi berhasil jika saya memperbarui kode untuk menambahkan metode lain:

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

Biarkan saya memperluas hanya untuk memperjelas. Kode di bawah ini menghasilkan ambiguitas:

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);
    }
}

Maka kode di bawah ini juga menghasilkan ambiguitas:

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);
    }
}

Namun yang ini tidak menghasilkan ambiguitas:

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);
    }
}
riruzen
sumber
2
Kompiler dapat memetakan printSum panggilan Anda (1, 2); baik printSum (panjang a, panjang b) atau printSum (int a, dobel b) karena itu ambigu. Anda harus secara eksplisit membantu kompiler untuk memilih di antara dengan memberikan tipe persis seperti ini: printSum (1, 2d)
Ravindra Ranwala
6
Anda salah mengutip pesan kesalahan, dan perbedaannya sangat penting. Pesan kesalahan yang sebenarnya adalah: Error:(15, 9) java: reference to printSum is ambiguous both method printSum(int,double) in ParamTest and method printSum(long,long) in ParamTest match- itu bukan metode yang ambigu, itu panggilan ke metode yang ambigu.
Erwin Bolwidt
1
@ ErwinBolwidt pesan kesalahan berasal dari gerhana maaf saya salah kutip di sana. Bagaimanapun, saya masih tidak memahaminya karena menambahkan printSum (int a, long b) menghapus pesan kesalahan.
riruzen
2
JLS-5.3 => Jika jenis ekspresi tidak dapat dikonversi ke jenis parameter dengan konversi yang diizinkan dalam konteks doa longgar, maka kesalahan waktu kompilasi terjadi. tampaknya berlaku untuk konteks, tetapi tidak benar-benar mudah untuk menyimpulkan caranya. +1
Naman
1
Kami pasti membutuhkan pertanyaan kanonik untuk ini: stackoverflow.com/… . "
Marco13

Jawaban:

17

Saya pikir ini ada hubungannya dengan aturan spesifik JLS tentang 15.12.2.5. Memilih Metode Paling Spesifik . Ini menyatakan bahwa:

Jika lebih dari satu metode anggota dapat diakses dan berlaku untuk pemanggilan metode, perlu memilih salah satu untuk memberikan deskriptor untuk pengiriman metode run-time. Bahasa pemrograman Java menggunakan aturan bahwa metode yang paling spesifik dipilih.

Bagaimana Java memilih metode yang paling spesifik dijelaskan lebih lanjut oleh teks:

Intuisi informal adalah bahwa satu metode lebih spesifik daripada yang lain jika setiap doa yang ditangani oleh metode pertama dapat diteruskan ke yang lain tanpa kesalahan waktu kompilasi. Dalam kasus-kasus seperti argumen ekspresi lambda yang diketik secara eksplisit (§15.27.1) atau pemanggilan arity variabel (§15.12.2.4), beberapa fleksibilitas diperbolehkan untuk mengadaptasi satu tanda tangan ke tanda tangan lainnya.

Dalam hal contoh Anda, semua metode dapat diakses dan berlaku untuk pemanggilan metode, oleh karena itu, Java perlu menentukan yang mana yang paling spesifik .

Untuk metode ini, tidak ada yang dapat ditentukan lebih spesifik:

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

Metode keempat membersihkan ambiguitas justru karena memenuhi kondisi yang diperlukan untuk menjadi paling spesifik .

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

Yaitu, (int, long) dapat diteruskan ke (int, double), (long, long), atau (double, long) tanpa kesalahan kompilasi.

kerusuhan
sumber
2
Jadi untuk summerize, jika semua metode dapat diakses oleh panggilan, maka java mengidentifikasi metode, yang dapat dipanggil ke metode lain tanpa kompilasi kesalahan waktu, jika ditemukan kemudian menggunakannya sebagai yang paling spesifik dan menyebutnya kesalahan ambiguitas? Saya pikir, ini adalah jawaban yang valid @riruzen.
Sandeep Kokate
Terima kasih! Saya mencoba ini dengan (ganda, int) vs (ganda, panjang) dan itu benar-benar membersihkan ambiguitas, kemudian (ganda, int) vs (panjang, ganda) ambigu seperti yang diharapkan.
riruzen
7

Ini memang pertanyaan yang sangat menarik. Mari kita pergi melalui Spesifikasi Bahasa Jawa langkah demi langkah.

  1. Ketika kompiler mencoba mengidentifikasi metode yang berpotensi berlaku, hal pertama yang dilakukan adalah melakukan serching untuk metode yang dapat diterapkan oleh Strict Invocation .

  2. Dalam kasus Anda tidak ada metode seperti itu, jadi langkah selanjutnya adalah menemukan metode yang dapat diterapkan oleh Loose Invocation

  3. Pada titik ini semua metode cocok, sehingga metode yang paling spesifik ( §15.12.2.5 ) dipilih di antara metode-metode yang dapat diterapkan dengan doa lepas.

Ini adalah momen kunci, jadi mari kita lihat ini dari dekat.

Satu metode yang berlaku m1 lebih spesifik daripada metode lain yang berlaku m2, untuk permohonan dengan ekspresi argumen e1, ..., ek, jika salah satu dari berikut ini benar:

(Kami hanya tertarik pada kasus berikut):

  • m2 bukan generik, dan m1 dan m2 berlaku dengan doa yang ketat atau longgar, dan di mana m1 memiliki tipe parameter formal S1, ..., Sn, dan m2 memiliki tipe parameter formal T1, ..., Tn, tipe Si lebih spesifik dari Ti untuk argumen ei untuk semua i (1 ≤ i ≤ n, n = k).

Sederhananya, suatu metode lebih spesifik jika semua tipe parameternya lebih spesifik . Dan

Tipe S lebih spesifik daripada tipe T untuk ekspresi apa pun jika S <: T ( §4.10 ).

Ekspresi S <: Tberarti Ssubtipe dari T. Untuk primitif kita memiliki hubungan sebagai berikut:

double > float > long > int

Jadi mari kita lihat metode Anda dan lihat mana yang lebih spesifik daripada yang lain.

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));
}

Dalam contoh ini parameter pertama metode 1 jelas lebih spesifik daripada parameter pertama metode 2 (jika Anda memanggilnya dengan nilai integer:) printSum(1, 2). Tetapi parameter kedua lebih spesifik untuk metode 2 , karena long < double. Jadi tidak satu pun dari metode ini lebih spesifik daripada yang lain. Itu sebabnya Anda memiliki ambiguitas di sini.

Dalam contoh berikut:

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));
}

tipe parameter pertama dari metode 1 lebih spesifik daripada yang ada di metode 2, karena int < longdan tipe parameter kedua sama untuk keduanya, itu sebabnya metode 1 dipilih.

Kirill Simonov
sumber
Saya tidak downvote jawaban Anda, tetapi jawaban ini tidak menjelaskan mengapa (int, double) ambigu dengan (panjang, panjang) karena jelas, (int, double) harus lebih spesifik berkenaan dengan parameter pertama.
riruzen
3
@riruzen menjelaskan: doubletidak lebih spesifik dari long. Dan untuk metode yang akan dipilih, semua parameter tipe harus lebih spesifik: tipe Si lebih spesifik daripada Ti untuk argumen ei untuk semua i (1 ≤ i ≤ n, n = k)
Kirill Simonov
Ini harus di atas +1
Teh
0

karena nilai int juga dapat dianggap sebagai ganda di java. berarti double a = 3valid dan sama dengan panjang long b = 3Jadi itu sebabnya ia menciptakan ambiguitas. Kamu panggil

printSum(1, 2);

Membingungkan untuk ketiga metode ini, karena ketiga metode ini valid:

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

Anda dapat menempatkan L di bagian akhir, untuk menentukan bahwa nilai itu panjang. sebagai contoh:

printSum(1L, 2L);

untuk gandakan Anda perlu mengubahnya:

printSum((double)1, 2L);

juga membaca komentar @Erwin Bolwidt

Sandeep Kokate
sumber
Ya kompiler bingung untuk semua 3 metode, kompiler pertama mencari jenis yang tepat dan jika tidak ditemukan maka cobalah untuk menemukan jenis yang kompatibel, dan dalam hal ini semua kompatibel.
Seorang programmer lain
2
Jika saya memiliki deklarasi metode 4 alih-alih 3, ambiguitas muncul: public static void printSum (int a, double b) public static void printSum (int a, long b) public static void printSum (long a, b panjang) public static batal printSum (double a, long b) jadi sementara saya setuju dengan jawaban Anda, masih tidak menjawab pertanyaan. Java melakukan promosi tipe otomatis jika tipe yang tepat tidak tersedia jika saya tidak salah. Saya hanya tidak mengerti mengapa itu menjadi ambigu dengan 3 orang itu.
riruzen