Upcasting و Downcasting

در برنامه نویسی شیء گرا، از تبدیل نوع (Type Cast) برای تغییر رفتار یک شیء استفاده می‌شود. با کلاس‌های Cat و Animal بحث را شروع می‌کنیم:

class Animal 
{
    
}
 
class Cat: Animal 
{
    
}

در کد بالا ما دو کلاس به نام های Animal و Cat ایجاد کرده ایم که کلاس Cat از کلاس Animal ارث بری کرده است. اکنون می‌توانیم اشیاء مورد نظرمان را از این کلاس‌ها ایجاد کنیم:

let cat: Cat              = Cat()
let animal: Animal        = Animal()
let anotherAnimal: Animal = Cat()

نکته جالبی که در اینجا وجود دارد این است که با اینکه شیء anotherAnimal از نوع Animal است اما در حقیقت یک شیء از Cat محسوب می‌شود. دلیل اینکه این کار را می‌توانیم انجام دهیم این است که هر Cat یک Animal نیز محسوب می‌شود. به این مفهوم چندریختی یا polymorphism گفته می‌شود که ردر درس آینده در مورد آن بیشتر توضیح می دهیم.

Upcasting چیست؟

upcasting به تبدیل کلاس فرزند (derived) به کلاس پدر (base) گفته می‌شود. به عبارت دیگر upcasting به ما اجازه می‌دهد تا با یک شیء از کلاس فرزند به عنوان یک شیء از کلاس پدر برخورد کنیم. برای مثال، کلاس Animal پدر کلاس Cat محسوب می‌شود، بنابراین می‌توانیم مقدار دهی زیر را انجام دهیم:

animal = cat

همچنین این امکان وجود دارد که با استفاده از کلمه کلیدی as این تبدیل را به صورت صریح نیز انجام دهیم:

animal = cat as Animal

Upcasting نتیجه رابطه is-a بین کلاس‌های فرزند و پدر است. رابطه is-a به این معناست که کلاس فرزند یک نوع از کلاس پدر است. برای مثال گربه یک نوع حیوان است پس رابطه is-a بین این دو وجود دارد.
شما با استفاده از کلمه کلیدی is می‌توانید نوع یک شیء را بررسی کنید. این عملگر یک مقدار Boolean بر می‌گرداند که نتیجه بررسی را مشخص می‌کند. برای مثال:

print(cat is Cat) //true

از آنجایی که در این مثال شیء cat از نوع کلاس Cat است، بنابراین این عملگر مقدار true را بر می‌گرداند. همچنین به این دلیل که کلاس Cat فرزند کلاس Animal محسوب می‌شود، بنابراین شیء cat را نیز از نوع Animal در نظر گرفته می‌شود:

print(cat is Animal) //true

Downcasting چیست؟

به فرآیندی که ما یک شیء از کلاس پدر را به یک شیء از کلاس فرزند تبدیل می‌کنیم Downcasting گفته می‌شود. برای انجام این کار باید حتماً از تبدیل صریح (cating) استفاده کرد. عمل Downcasting را همیشه نمی‌توان انجام داد. دلیل این محدودیت این است که در بیشتر موارد رابطه is-a متقارن نیست. برای مثال گربه یک حیوان است ولی هر حیوانی گربه نیست و این به معنی عدم تقارن در رابطه is-a است. به مثال زیر توجه کنید:

print(anotherAnimal is Cat) //true

در اینجا چون شیء anotherAnimal از نوع Cat است بنابراین عملگر is مقدار true را بر می‌گرداند. اما شما به سادگی نمی‌توانید شیء anotherAnimal را در شیء cat قرار دهید و با خطای کامپایلر رو به رو می‌شوید:

Cat = anotherAnimal //Compiler Error

برای اینکه این مقداردهی را انجام دهید استفاده از عملگر as کافی نیست و باید از !as یا ?as استفاده کنید. زمانی از عملگر !as استفاده کنید که از ممکن بودن تبدیل اطمینان دارید:

if anotherAnimal is Cat 
{
    cat = anotherAnimal as! Cat
}

اگر تبدیل با استفاده از !as امکان پذیر نباشد با خطای حین اجرا رو به رو خواهید شد. عملگر ?as فقط زمانی تبدیل را انجام می‌دهد که این تبدیل امکان پذیر باشد، در غیر این صورت مقدار nil (null) را بر می‌گرداند. بنابراین استفاده از آن در مواردی که از امکان تبدیل اطمینان ندارید کاربرد زیادی دارد:

if let anotherAnimal = anotherAnimal as? Cat 
{
    cat = anotherAnimal
}