۳ خرداد ۱۴۰۴
توی این قسمت با جنریک ها و نحوه استفاده از آنها آشنا میشیم.
پشتیبانی از Generics در نسخه ۱.۱۸ به گولنگ اضافه شد. Generics یا نوعهای پارامتریک (Parametric Polymorphism) یکی از ویژگیهایی است که امکان نوشتن توابع، ساختارهایی را فراهم میکند که میتوانند با انواع مختلف داده کار کنند بدون اینکه مجبور باشیم برای هر نوع داده، کد جداگانهای بنویسیم.
تا قبل از معرفی جنریک ها, اگر نیاز داشتیم تابعی داشته باشیم که با نوع های مختلفی از داده ها کار کند باید به ازای هر نوع داده, کدهای تابع را چندین بار تکرار میکردیم.
برای مثال فرض کنید میخواهید تابعی داشته باشید که دو عدد را به عنوان پارامتر ورودی میگیرد و از بین این دو عدد بزرگترین مقدار را برمیگرداند. این تابع رو میتوانیم به صورت زیر برای نوع های int و float64 داشته باشیم:
در مثال بالا به دلیل اینکه هرکدام از تابع های ما نمیتوانند هر دو نوع float64 و int رو عنوان پارامتر ورودی یا خروجی بپذیرند, مجبور شدیم دوبار یک کد کاملا یکسان رو تکرار کنیم.
برای اینکه در مثال قبل از تکرار پرهیز کنیم راه حل های دیگری نیز وجود دارد:
هرکدوم از روش های بالا مشکلاتی دارند و راه حل تمیزی نیستن. برای مثال اگر از type assertion / interface assertion استفاده کنیم کد ما به صورت زیر خواهد بود:
کد بالا به درستی کار میکند اما مشکلاتی دارد:
استفاده از any باعث میشود که خوانایی برنامه پایین بیاید: در هنگام استفاده از تابع max باید ابتدا پیاده سازی آن را چک کنیم تا متوجه شویم چه دیتا تایپ هایی را پشتیبانی میکند.
دشوار شدن نگهداری و توسعه ی کد: در هنگام کامپایل نمیتوان خطاهای احتمالی را کشف کرد. در صورتی که اشتباها تابع max را با نوعی از داده کال کنیم که آن را پیاده سازی نمیکند با panic در زمان اجرای برنامه (runtime) مواجهه خواهیم شد.
int و float64 تکرار شده است.جنریک در Go به کمک type parameters تعریف میشود. یعنی میتوان برای تابع، نوعی کلی تعریف کرد و بعداً هنگام استفاده، نوع واقعی را مشخص کرد.
با استفاده از Generics میتوانیم تابع max را به صورت زیر بازنویسی کنیم:
در مثال بالا [int |float] لیستِ نوعِ آرگومان ها است, همچنین int و float هرکدام یک نوعِ آرگومان محسوب میشوند. بنابراین نوعِ T میتواند هرکدام از نوع های float یا int باشد. تابع max یک نوعِ پارامتریک یا جنریک محسوب میشود.
در هنگام استفاده از تابع max میتوانیم نوعِ پارامترهای ورودی و خروجی را با مشخص کردن نوع T کنترل کنیم. در مثال زیر نوع پارامتر T را int در نظر گرفتیم بنابراین هردو پارامتر ورودی تابع باید از نوع int باشند, در غیر اینصورت خطای زمان کامپایل رخ میدهد.
با استفاده از پارامتر لیست میتوان تایپ های جنریک ایجاد کرد. فرض کنید میخواهیم یک slice جنریک به نام List[T] داشته باشم, میتوانیم آن را به صورت زیر پیاده سازی کنیم:
اگر نوع داده, جنریک باشد (مثل List[T]), میتوان متدهایی نوشت که از پارامتر تایپ های آن استفاده کنند.
بر خلافِ تابع ها, متدها نمیتوانند دارای لیست پارامتر تایپ باشند اما میتوانند از پارامتر تایپِ نوعِ دادهی خود استفاده کنند, به عنوان مثال کد زیر با خطای زمان کامپایل مواجه میشود:
قسمت قبل: اینترفیس (interface) | گولنگ به زبان ساده
قسمت بعد: Defer , Panic و Recover | گولنگ به زبان ساده