What is function overloading?

Jul 17, 2024//-4 min

Function overloading, birçok programlama dilinde mevcut olan bir özelliktir. Aynı davranışı sergileyen ve aynı adı taşıyan birden fazla fonksiyon tanımlamanıza olanak tanır. Derleyici tarafından parametre sayısı ve tipiyle birbirinden ayrıştırılır. Bu özellik sayesinde kodun kullanılabilirliği, okunabilirliğini ve esnekliği artmış olur.

Function overloading in OOP languages

Java ve C++ gibi öz hakiki OOP dillerinde ana konseptlerden biridir. Derleyici doğru fonksiyonu çağırmak için fonksiyon ismi ve parametre türlerinden oluşan signature'a bakarak karar verir.

Java example

Java'da implementasyonu aşağıdaki gibidir:

1class Notification {
2  void send(String email, String message) {
3    // ...
4  }
5
6  void send(String email, String message, boolean isUrgent) {
7    // ...
8  }
9
10  void send(String email, String subject, String body) {
11    // ...
12  }
13}
14
15public class Main {
16  public static void main(String[] args) {
17    Notification notifier = new Notification();
18
19    notifier.send("Hello!");
20    notifier.send(
21      "Reminder",
22      "The meeting time has been changed to 10 AM."
23    );
24    notifier.send(
25      "ensbaspinar@gmail.com",
26      "Can you send me the plans for the team?",
27      true
28    );
29  }
30}

C++ example

C++'da da benzer bir implementasyon mevcuttur:

1class Notification {
2public:
3  void send(const string& message) {
4    // ...
5  }
6
7  void send(const string& subject, const string& body) {
8    // ...
9  }
10
11  void send(const string& email, const string& message, bool isUrgent) {
12    // ...
13  }
14};
15
16int main() {
17  Notification notifier;
18
19  notifier.send("Hello!");
20  notifier.send(
21    "Reminder",
22    "The meeting time has been changed to 10 AM."
23  );
24  notifier.send(
25    "ensbaspinar@gmail.com",
26    "Can you send me the plans for the team?",
27    true
28  );
29}

Her iki dilde de overload resolution derleme zamanında yapılır, bu da sağlanan argümanlara göre uygun yöntemin çağrılmasını kesinleştirir.

How to handles function overloading?

  1. Function Signatures
    Derleyici, aynı isme sahip fonksiyonların signature'larını kontrol eder. Eğer bu fonksiyonların parametre listeleri farklıysa, derleme başarılı olur; aksi takdirde derleme hatası oluşur.
  2. Name Mangling
    Aynı isme sahip fonksiyonları ayırt etmek için name mangling tekniğini kullanır. Bu teknik, fonksiyon adlarını parametre türleri ve sayılarıyla birlikte kodlayarak benzersiz hale getirir. Örneğin,
    foo(int, double)
    ve
    foo(double, double)
    fonksiyonlarının isimleri derleyicide
    foo_id
    ve
    foo_dd
    olarak temsil edilir.
  3. Call Resolution
    Fonksiyon çağrıldığında derleyici argüman türlerini kontrol eder ve uygun fonksiyonu bulur.

Function overloading in TypeScript

TypeScript, JavaScript'in statik tiplerle genişletilmiş üst kümesi olarak function overloading destekler ancak bu işlemi geleneksel OOP dillerinden farklı ele alır. Birden fazla fonksiyon signature'ı tanımlanır ancak implementasyon tek fonksiyonda yapılır.

Example

1class Notification {
2  send(message: string): void;
3  send(subject: string, body: string): void;
4  send(email: string, message: string, isUrgent?: boolean): void;
5
6  send(arg1: unknown, arg2?: unknown, arg3?: unknown): void {
7    if (typeof arg1 === "string" && !arg2 && !arg3) {
8      // ...
9    } else if (typeof arg1 === "string" && typeof arg2 === "string" && !arg3) {
10      // ...
11    } else if (typeof arg1 === "string" && typeof arg2 === "string" && typeof arg3 === "boolean") {
12      // ...
13    }
14  }
15}
16
17const notifier = new Notification();
18
19notifier.send("Hello!");
20notifier.send("Hello!");
21notifier.send(
22  "Reminder",
23  "The meeting time has been changed to 10 AM."
24);
25notifier.send(
26  "ensbaspinar@gmail.com",
27  "Can you send me the plans for the team?",
28  true
29);

Example 2

In the second example, let's see how to change the behavior in a hook we created in React.

1type Callback = () => void;
2
3export function useSeenObserver(callback: Callback): React.RefObject<HTMLElement>;
4export function useSeenObserver(selector: string, callback: Callback): void;
5export function useSeenObserver(arg1: string | Callback, arg2?: Callback): React.RefObject<HTMLElement> | void {
6  let element: Element;
7  let callback: Callback;
8  let ref = useRef<HTMLElement>(null);
9
10  if (typeof arg1 === "string") {
11    element = document.querySelector(arg1)!;
12    callback = arg2!;
13  } else {
14    element = ref.current!;
15    callback = arg1!;
16  }
17
18  // ...
19
20  if (typeof arg1 !== "string") {
21    return ref;
22  }
23}
24
25/* Usage 1 */
26const ref = useSeenObserver(() => {
27  // ...
28});
29
30/* Usage 2 */
31useSeenObserver("#component", () => {
32  // ...
33});

How to handles function overloading?

  1. Checking Function Signatures
    Fonksiyonun belirtilen signature'lardan biriyle uyumlu olup olmadığını görmek için derleme zamanında türü kontrol eder. Aynı zamanda geliştirme esnasında statik kontroller gerçekleştirerek uyumsuzluklar hakkında uyarır.
  2. Call Resolution
    Typescript, JavaScript'e derlendiğinden dolayı type safety ve signature check işlemleri run-time'da çalışmaz. Bu sebepten ötürü fonksiyonun içerisinde parametre türlerinin koşullarla kontrol edilmesi gerekir.

Why should I use it?

Typescript'te union kullanmak yerine neden signature kullanmalıyım sorusu aklınıza gelebilir. Son örneği signature kullanmadan yeniden yazalım.

1type Callback = () => void;
2
3export function useSeenObserver(arg1: string | Callback, arg2?: Callback): React.RefObject<HTMLElement> | void {
4  let element: Element;
5  let callback: Callback;
6  let ref = useRef<HTMLElement>(null);
7
8  if (typeof arg1 === "string") {
9    element = document.querySelector(arg1)!;
10    callback = arg2!;
11  } else {
12    element = ref.current!;
13    callback = arg1!;
14  }
15
16  // ...
17
18  if (typeof arg1 !== "string") {
19    return ref;
20  }
21}

Daha sonra, `useSeenObserver' hook'unu kullandığımızda TypeScript'in önerdiği tipe ve yaptığı kontrole bakalım.

Görebileceğiniz gibi net bir function overloading template veremez. Argüman isimleri anlamsız kalır. Hangi parametrenin hangisiyle birlikte kullanılması gerektiği konusu bulanıklaşır. Aşağıdaki varyantlarda, hatalı kullanımlar da dahil, TypeScript hata vermez:

  • (arg1: string)
    -> hatalı kullanım
  • (arg1: string, arg2: Callback)
  • (arg1: Callback)
  • (arg1: Callback, arg2: Callback)
    -> hatalı kullanım

Signature kullandığınızda ise aşağıdaki gibi doğru bir API elde etmiş olursunuz.