Eyni ada, lakin müxtəlif parametr listinə (different method signature) sahib olan metodlar overload metodlar adlanır. Signature`nin tərkibinə daxildir:
- metod adı;
- parametr siyahısı.
Overload metodlarda metod adından başqa hər şey fərqli ola bilər (məsələn: access modifiers, specifiers (like static), return types və exception lists).
Overload metodlar əsasən parametr listinə görə təyin olunur, yəni bu metodlar üçün vacib qayda budur ki, ad eyni olsun, parametr listi fərqli. Fərqli dedikdə nə nəzərdə tutulur:
- parametrlərin sayı müxtəlif olmalıdır;
- parametrlərin sayı eyni ola bilər, o halda ki:
- tipləri fərqli olmalıdır;
- ardıcılıq fərqli olmalıdır.
Nümunə:
public void fly(int i) {} public void fly(short s) {} public boolean fly() { return false; } void fly(int i, short s) {} public void fly(short s, int i) throws Exception {} int fly(int i, short s){ return 0; } // doesn’t compile, for line 8 public static final void fly(short a, int b){} // doesn’t compile, for line 9
Overloading and Varargs
Java varargs`la massiv kimi davranır. Əgər biz parametr olaraq int[]
göndərsək, hansı metod çalışacaq?
public void fly(int[] array) {} public void fly(int... varargs) {} // DOES NOT COMPILE
2-ci metod compile olunmayacaq, çünki java bunların hər ikisi üçün metod signature`ni eyni cür görür. Əgər ayrı-ayrılıqda compile olunsa, parametr olaraq massiv göndərilsə hər iki metod çalışacaq. Yalnız varargs`ın çalışmasını istəyiriksə, massiv elementlərini tək-tək göndəririk:
fly(new int[] {1, 2, 3} ); // both of them fly(1, 2, 3); // only varargs
Autoboxing
public void fly(int i){} public void fly(Integer i){}
Əgər bizim int
və Integer
parametr qəbul edən metodumuz varsa, onu fly(5);
olaraq çağırsaq 1-ci metod işləyəcək, 1-ci metod olmasa, o zaman 2-ci metod çağırılacaq. Çünki java həmişə ilk olaraq dəqiq uyğunluq olan parametri tapmağa çalışır. Əgər int
parametrli metod mövcuddursa, heç bir səbəb yoxdur ki, java int
`i İnteger
`ə autoboxing edərək əlavə iş görsün. Əgər Integer i = 5;
elan edib i
dəyişənini fly
metoduna göndərsək o zaman 2-ci metod işləyəcək.
Primitive and Reference Types
Qeyd etdiyimiz kimi birinci dəqiq uyğunluq tapılır, sonra digər etaplar nəzərdən keçirilir. Aşağıdakı kod nümunəsində əgər bizim int
parametr qəbul edən metodumuz olmasa, long
parametrli metod çağırılır, çünki daha geniş tipə malik parametrli metodu çağırmaq java üçün problem deyil, amma əksi mümkün deyil, çünki java daha kiçik tipə avtomatik çevirmə etmir.
class OverloadingTest { public void fly(int i) { System.out.print("int "); } public void fly(long l) { System.out.print("long "); } public void fly(Object o) { System.out.print("object "); } public static void main(String[] args) { OverloadingTest obj = new OverloadingTest(); obj.fly(123); obj.fly(123L); obj.fly((short) 123); obj.fly(123.0); } }
Output:
int long int object
Main metodda sonuncu sətirdə double
tipli parametr göndəririk. İlk öncə double
tipi axtarılır, dəqiq uyğunluq tapılmadığına görə double
autoboxing olaraq Double
tipinə çevirilir. Və yenə dəqiq uyğunluq tapılmadığına görə Object
parametrli metod çağırılır.
Putting It All Together
Overload metodların işləmə ardıcıllığı aşağıda cədvəldə göstərilib*:
class TestOverloading { public static void main(String[] args) { System.out.print(glide()); System.out.print(glide("a")); System.out.print(glide("a", "b")); System.out.print(glide("a", "b", "c")); } public static String glide(String s) { return "1"; } public static String glide(String... s) { return "2"; } public static String glide(Object o) { return "3"; } public static String glide(String s, String t) { return "4"; } }
Output:
2142
Varargs parametrli metoda boş parametr də göndərə bilərik, bu zaman java boş massiv göndərir.
Java ən dəqiq uyğunluq olan parametri axtaran zaman ancaq bir çevirmə (one conversion) edir.
public class TooManyConversions { // public static void play(Object o) {} public static void play(Long l) {} public static void play(Long... l) {} public static void main(String[] args) { play(4); // DOES NOT COMPILE play(4L); // calls the Long version } }
Yuxarıdakı nümunədə problem çevirmə ilə bağlıdır. Java int 4`ü long 4 və ya Integer 4`ə rahatlıqla çevirir. Amma bir addıma int`i Long`a çevirə bilmir, gərək əvvəlcə int`i long`a çevirsin, sonra da long`u Long`a, yəni iki addıma. İki çevirmə də mümkün deyil. Amma əgər 1-ci metodu commentdən çıxarsaq, kod compile olunacaq. Çünki bu zaman ancaq bir çevrilmə yerinə yetiriləcək: int`dən Integer`ə. Integer də obyekt olduğundan birinci metod çağırılacaq.
int
parametr qəbul edən metod həm char
, həm də int
tipini çağıra bilir. Amma char
tipində parametr qəbul edən metod int
tipini çağıra bilmir. Aşağıdakı nümunəyə Enthuware testlərində rast gələ bilərsiniz:
class Noobs { public void m(int a) { System.out.println("In int "); } // line1 public void m(char c) { System.out.println("In char "); } // line2 public static void main(String[] args) { Noobs n = new Noobs(); int a = 'a'; char c = 6; n.m(a); // line3 n.m(c); // line4 } }
Əgər line2 commentə alınsa kod normal işləyəcək, amma line1 commentə alınsa line3 compile xətası verəcək.
Enthuware sual bankından daha bir maraqlı nümunə ilə tanış olaq və səbəbini öyrənək:
class TestClass { public void method(Object o){ System.out.println("Object Version"); } public void method(java.io.FileNotFoundException s){ System.out.println("java.io.FileNotFoundException Version"); } public void method(java.io.IOException s){ System.out.println("IOException Version"); } public static void main(String args[]){ TestClass tc = new TestClass(); tc.method(null); } }
Sualdan da aydın göründüyü kimi hər üç metod null
parametrini qəbul edir, bəs maraqlıdır ki, kodu run etsək hansı metod çalışacaq? Əvvəlki yazılarımızda da qeyd etdik ki, Java birinci ən dəqiq uyğunluğu (the most specific) axtarır. Bildiyimiz kimi FileNotFoundException
classı IOException
classının subclass`ıdır, ona görə də bu nümunədə “the most specific class” FileNotFoundException
classı hesab edilir.
Əgər “most specific class” sayı bir deyil iki olarsa, onda nə baş verəcək? Yuxarıdakı kod nümunəsini biraz fərqli şəkildə yazaq və nəticəyə baxaq:
public class TestClass { public void method(Object o){ System.out.println("Object Version"); } public void method(String s){ System.out.println("String Version"); } public void method(StringBuilder s){ System.out.println("StringBuilder Version"); } public static void main(String args[]){ TestClass tc = new TestClass(); tc.method(null); // DOES NOT COMPILE } }
Aşağıdakı compile xətası ilə qarşılaşacağıq:
reference to method is ambiguous
both method method(String) in TestClass and method method(StringBuilder) in TestClass match
Məhz bu səbəbdən kod sətrinə System.out.println(null);
yazdıqda compile xətası verir, çünki println()
overload metoddur və parametr olaraq null
göndərdikdə birdən çox uyğunluq tapılır.
Mövzunu tam dərk etdiyinizə əmin olmaq üçün sonda Enthuware-dən daha bir kod nümunəsi qeyd edəcəm, çox maraqlı sualdır. Kod nümunəsini dəyişərək ağlınıza gələn bir neçə kombinasiyada yoxlaya bilərsiniz. Belə olduqda sizə qaranlıq qalan məqamları özünüz üçün aydınlatmış olacaqsınız. Suallarınız yaranarsa, commentdə qeyd edə bilərsiniz.
class TestClass { void probe(int... x) { System.out.println("In ..."); } void probe(Integer x) { System.out.println("In Integer"); } void probe(long x) { System.out.println("In long"); } void probe(Long x) { System.out.println("In LONG"); } public static void main(String[] args){ Integer a = 4; new TestClass().probe(a); int b = 4; new TestClass().probe(b); } }
[topics lang=az]