Apakah Tanda Tangan Metode Termasuk Jenis Pengembalian di Java?

1. Ikhtisar

Tanda tangan metode hanya sebagian dari seluruh definisi metode di Java. Dengan demikian, anatomi yang tepat dari tanda tangan dapat menyebabkan kebingungan.

Dalam tutorial ini, kita akan mempelajari elemen tanda tangan metode dan implikasinya dalam pemrograman Java.

2. Metode Tanda Tangan

Metode di Java mendukung overloading, yang berarti bahwa beberapa metode dengan nama yang sama dapat ditentukan dalam kelas atau hierarki kelas yang sama. Oleh karena itu, kompilator harus dapat mengikat secara statis metode yang dirujuk oleh kode klien. Untuk alasan ini, tanda tangan metode secara unik mengidentifikasi setiap metode .

Menurut Oracle, tanda tangan metode terdiri dari nama dan jenis parameter . Oleh karena itu, semua elemen deklarasi metode lainnya, seperti pengubah, tipe kembalian, nama parameter, daftar pengecualian, dan isi bukan bagian dari tanda tangan.

Mari kita lihat lebih dekat metode overloading dan bagaimana hubungannya dengan tanda tangan metode.

3. Kesalahan Beban Berlebih

Mari perhatikan kode berikut :

public void print() { System.out.println("Signature is: print()"); } public void print(int parameter) { System.out.println("Signature is: print(int)"); }

Seperti yang bisa kita lihat, kode dikompilasi karena metode memiliki daftar tipe parameter yang berbeda. Akibatnya, kompilator dapat secara deterministik mengikat setiap panggilan ke satu atau yang lain.

Sekarang mari kita uji apakah legal untuk membebani dengan menambahkan metode berikut:

public int print() { System.out.println("Signature is: print()"); return 0; }

Saat kita mengkompilasi, kita mendapatkan kesalahan "metode sudah ditentukan di kelas". Itu membuktikan bahwa tipe pengembalian metode bukan bagian dari tanda tangan metode .

Mari kita coba hal yang sama dengan pengubah:

private final void print() { System.out.println("Signature is: print()"); }

Kami masih melihat kesalahan "metode sudah ditentukan di kelas" yang sama. Oleh karena itu, tanda tangan metode tidak bergantung pada pengubah .

Overloading dengan mengubah pengecualian yang dilempar dapat diuji dengan menambahkan:

public void print() throws IllegalStateException { System.out.println("Signature is: print()"); throw new IllegalStateException(); }

Sekali lagi kita melihat kesalahan "metode sudah ditentukan di kelas", yang menunjukkan deklarasi lemparan tidak bisa menjadi bagian dari tanda tangan .

Hal terakhir yang dapat kita uji adalah apakah mengubah nama parameter memungkinkan terjadinya overloading. Mari tambahkan metode berikut:

public void print(int anotherParameter) { System.out.println("Signature is: print(int)"); }

Seperti yang diharapkan, kami mendapatkan kesalahan kompilasi yang sama. Ini berarti nama parameter tidak mempengaruhi tanda tangan metode .

3. Generik dan Jenis Penghapusan

Dengan parameter umum , penghapusan jenis mengubah tanda tangan efektif . Akibatnya, ini dapat menyebabkan tabrakan dengan metode lain yang menggunakan batas atas tipe generik, bukan token generik.

Mari perhatikan kode berikut:

public class OverloadingErrors { public void printElement(T t) { System.out.println("Signature is: printElement(T)"); } public void printElement(Serializable o) { System.out.println("Signature is: printElement(Serializable)"); } }

Meskipun tanda tangan tampak berbeda, kompilator tidak dapat mengikat secara statis metode yang benar setelah penghapusan jenis.

Kita dapat melihat kompilator mengganti T dengan batas atas, Dapat diserialisasi, karena penghapusan tipe. Jadi, itu bentrok dengan metode secara eksplisit menggunakan Serializable .

Kita akan melihat hasil yang sama dengan Object tipe dasar ketika tipe generik tidak memiliki batasan.

4. Daftar Parameter dan Polimorfisme

Tanda tangan metode memperhitungkan jenis yang tepat. Itu berarti kita bisa membebani metode yang tipe parameternya adalah subclass atau superclass.

Namun, kita harus memberi perhatian khusus karena pengikatan statis akan mencoba mencocokkan menggunakan polimorfisme, tinju otomatis, dan promosi tipe .

Mari kita lihat kode berikut:

public Number sum(Integer term1, Integer term2) { System.out.println("Adding integers"); return term1 + term2; } public Number sum(Number term1, Number term2) { System.out.println("Adding numbers"); return term1.doubleValue() + term2.doubleValue(); } public Number sum(Object term1, Object term2) { System.out.println("Adding objects"); return term1.hashCode() + term2.hashCode(); }

Kode di atas benar-benar legal dan akan dikompilasi. Kebingungan mungkin muncul saat memanggil metode ini karena kita tidak hanya perlu mengetahui tanda tangan metode yang tepat yang kita panggil, tetapi juga bagaimana Java secara statis mengikat berdasarkan nilai sebenarnya.

Mari jelajahi beberapa pemanggilan metode yang akhirnya terikat pada penjumlahan (Integer, Integer) :

StaticBinding obj = new StaticBinding(); obj.sum(Integer.valueOf(2), Integer.valueOf(3)); obj.sum(2, 3); obj.sum(2, 0x1);

Untuk panggilan pertama, kami memiliki jenis parameter yang tepat Integer, Integer. Pada panggilan kedua, Java akan secara otomatis memasukkan int ke Integer untuk kita . Terakhir, Java akan mengubah nilai byte 0x1 menjadi int dengan cara promosi tipe dan kemudian meng-auto- boxnya menjadi Integer.

Demikian pula, kami memiliki panggilan berikut yang mengikat ke jumlah (Angka, Angka) :

obj.sum(2.0d, 3.0d); obj.sum(Float.valueOf(2), Float.valueOf(3));

Pada panggilan pertama, kami memiliki nilai ganda yang otomatis dimasukkan ke kotak ganda. Dan kemudian, melalui polimorfisme, Ganda mencocokkan Angka. Secara identik, Float mencocokkan Nomor untuk panggilan kedua.

Let's observe the fact that both Float and Double inherit from Number and Object. However, the default binding is to Number. This is due to the fact that Java will automatically match to the nearest super-types that match a method signature.

Now let's consider the following method call:

obj.sum(2, "John");

In this example, we have an int to Integer auto-box for the first parameter. However, there is no sum(Integer, String) overload for this method name. Consequentially, Java will run through all the parameter super-types to cast from the nearest parent to Object until it finds a match. In this case, it binds to sum(Object, Object).

To change the default binding, we can use explicit parameter casting as follows:

obj.sum((Object) 2, (Object) 3); obj.sum((Number) 2, (Number) 3);

5. Vararg Parameters

Now let's turn our attention over to how varargs impact the method's effective signature and static binding.

Here we have an overloaded method using varargs:

public Number sum(Object term1, Object term2) { System.out.println("Adding objects"); return term1.hashCode() + term2.hashCode(); } public Number sum(Object term1, Object... term2) { System.out.println("Adding variable arguments: " + term2.length); int result = term1.hashCode(); for (Object o : term2) { result += o.hashCode(); } return result; }

So what are the effective signatures of the methods? We've already seen that sum(Object, Object) is the signature for the first. Variable arguments are essentially arrays, so the effective signature for the second after compilation is sum(Object, Object[]).

A tricky question is how can we choose the method binding when we have just two parameters?

Let's consider the following calls:

obj.sum(new Object(), new Object()); obj.sum(new Object(), new Object(), new Object()); obj.sum(new Object(), new Object[]{new Object()});

Obviously, the first call will bind to sum(Object, Object) and the second to sum(Object, Object[]). To force Java to call the second method with two objects, we must wrap it in an array as in the third call.

The last thing to note here is that declaring the following method will clash with the vararg version:

public Number sum(Object term1, Object[] term2) { // ... }

6. Conclusion

In this tutorial, we learned that the method signatures are comprised of the name and the parameter types' list. The modifiers, return type, parameter names, and exception list cannot differentiate between overloaded methods and, thus, are not part of the signature.

We've also looked at how type erasure and varargs hide the effective method signature and how we can override Java's static method binding.

Seperti biasa, semua contoh kode yang ditampilkan dalam artikel ini tersedia di GitHub.