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