آموزش جاوا – قسمت 12 (مفهوم Generic)

شاید بعد از 11 قسمت جلسه آموزشی جاوا که با typeهای مختلف داده ها کار کردید به ذهنتان رسیده باشد که چقدر خوب می‌شد اگر می‌توانستیم توابع را طوری تعریف کنیم که دیگر دغدغه type آرگومان آن را هنگام صدا زدن تابع نداشتیم. خوب باید بگم که این آرزوی شما خیلی هم بی‌راه و دور از ذهن نیست و قبل از شما آروزی خیلی‌های دیگه بوده… و خبر خوب اینکه با مفهوم Generic تا حدی امکان‌پذیر است! و اما چگونه؟

کلاس‌ها و توابع Generic در جاوا به برنامه‌نویسان این قابلیت را می‌دهند تا با تعریف یک تابع یا یک کلاس، یک مجموعه تابع یا مجموعه type به هم مرتبط را همزمان تعریف کنند.در واقع با این مفهوم شما خواهید توانست تا یک عملیات راروی یک آرایه از Objectها (چه از نوع integer، چه double، چه String و …)  انجام دهید.

 

توابع Generic:

یکی از نیازهای ما برنامه‌نویسان این است که بتوایم توابعی را تعریف کنیم که بتوان آن‌ها را با آرگومان‌هایی از نوع مختلف صدا زد و کامپایلر بر اساس نوع آن آرگومان بتواند آن‌ها را کامپایل کند. قبل از اینکه این تصور به شما دست بدهد که برنامه‌نویسی جاوا انقد هرکی هرکیه(!) بهتر است بدانید که هر دور زدنی قواعد خاص خودش را دارد! پس شما را با شماری از قواعد استفاده از این مفهوم آشنا می‌کنم:

  • تمام توابعی که به صورت generic تعریف می‌شوند یک بخش برای type پارامتر دارند که داخل <> مشخص می‌شود.
  • می‌توان داخل <>، بیش از یک پارامتر type مشخص کرد. که در این صورت با کاما از هم جدا می‌شوند. این پارامترها در واقع نام یا شناسه‌ای برای آن نوع داده عمومی (generic) هستند.
  • از این پارامترها می‌توان برای تعریف type بازگشتی تابع استفاده کرد که در این صورت به عنوان محل نگهداری برای type آرگومان پاس داده شده به تابع generic عمل خواهد کرد.
  • بدنه تابع generic مشابه توابع معمولی تعریف می‌شود. بنابراین جای هیچ نگرانی نیست. توجه داشته باشید که این پارامترها صرفا برای Reference typeها مورد استفاده قرار می‌گیرند، و برای primitive typeها (مثل int، double، char) قابل استفاده نیستند.

حال که با مفهوم Genericها آشنا شدید، بد نیست تا یک مثال را باهم بررسی کنیم:

public class GenericMethodTest {
   // generic method printArray
   public static < E > void printArray( E[] inputArray ) {
      // Display array elements
      for(E element : inputArray) {
         System.out.printf("%s ", element);
      }
      System.out.println();
   }

   public static void main(String args[]) {
      // Create arrays of Integer, Double and Character
      Integer[] intArray = { 1, 2, 3, 4, 5 };
      Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
      Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

      System.out.println("Array integerArray contains:");
      printArray(intArray);   // pass an Integer array

      System.out.println("\nArray doubleArray contains:");
      printArray(doubleArray);   // pass a Double array

      System.out.println("\nArray characterArray contains:");
      printArray(charArray);   // pass a Character array
   }
}

همانطور که در کد بالا مشاهده می‌شود از E به عنوان نام یا شناسه یک پارامتر type به خصوص استفاده شده است. با تعریف این تابع به صورت generic توانسته‌ایم با یک تیر سه نشان زده و  هم آرایه‌های integer، هم آرایه‌های double ، و هم آرایه‌های character را چاپ کنیم.

خروجی حاصل از اجرای کد بالا به این صورت خواهد بود:

Array integerArray contains:
1 2 3 4 5 

Array doubleArray contains:
1.1 2.2 3.3 4.4 

Array characterArray contains:
H E L L O

 

پارامترهای type محدود شده:

ممکن است مواقعی نیاز داشته باشید تا انواع typeهای ممکن برای پاس دادن به یک پارامتر type را محدود کنید. به عنوان مثال تابعی که عملیاتی را روی اعداد انجام می‌دهد، نباید بتواند آرگومانی از جنس رشته بگیرد، بلکه فقط آرگومان‌هایی از جنس Number یا زیرکلاس‌هایی از آن را بگیرد. این همان کاریست که پارامترهای type محدودشده یا bounded type parameters انجام می‌دهند.

برای تعریف چنین پارامترهایی کافیست به ترتیب اسامی آن‌ها، کلمه کلیدی extends ، و در آخر محدودیت مورد نظر را بنویسید. (مثل: <U extends Number>)

برای اینکه بهتر متوجه این مطلب شوید، توجه شما را به مثال زیر جلب می‌کنم:

public class MaximumTest {
   // determines the largest of three Comparable objects
   
   public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
      T max = x;   // assume x is initially the largest
      
      if(y.compareTo(max) > 0) {
         max = y;   // y is the largest so far
      }
      
      if(z.compareTo(max) > 0) {
         max = z;   // z is the largest now                 
      }
      return max;   // returns the largest object   
   }
   
   public static void main(String args[]) {
      System.out.printf("Max of %d, %d and %d is %d\n\n", 
         3, 4, 5, maximum( 3, 4, 5 ));

      System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n",
         6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ));

      System.out.printf("Max of %s, %s and %s is %s\n","pear",
         "apple", "orange", maximum("pear", "apple", "orange"));
   }
}

در کد بالا بسته به نوع داده‌ای که هنگام صدا زدن تابع generic استفاده می‌شود، بزرگترین عنصر آن آرایه برگردانده می‌شود. محدودیت استفاده شده بعد از کلمه کلیدی extends به این معنی است که قرار است آرگومان‌هایی به عنوان ورودی استفاده شوند که قابل مقایسه هستند.(پس ما نمی‌توانیم به عنوان ورودی، ترکیبی از اعداد و رشته بدهیم).

خروجی نهایی کد بالا به این صورت خواهد بود:

Max of 3, 4 and 5 is 5

Max of 6.6,8.8 and 7.7 is 8.8

Max of pear, apple and orange is pear

 

کلاس‌های Generic:

تعریف کلاس‌های generic کاملا مشابه کلاس‌های معمولی است. با این تفاوت که به دنبال نام کلاس، یک بخش برای پارامتر type آورده می‌شود.

مشابه توابع generic، بخش مربوط به این پارامتر می‌تواند شامل بیش از یک پارامتر باشد که با کاما از یکدیگر جدا شده‌اند.

در زیر مثالی از یک کلاس Generic آورده شده است. برای درک بهتر، به مثال زیر توجه کنید:

public class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }

   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<String> stringBox = new Box<String>();
    
      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

همانطور که مشاهده می‌شود از کلاس box، دو instance با typeهای مختلف ساخته شده است. integerBox از نوع Integer و stringBox از نوع String. و این یعنی جادویی به نام Generic…

خروجی نهایی این کد به صورت زیر خواهد بود:

Integer Value :10
String Value :Hello World

 

در ابن بخش سعی کردیم تا شما را با مفهوم Generic و کاربرد آن آشنا کنیم. حتی اگر نیازی به استفاده از این مفهوم در کدهای خود نبینید، قطعا در کدهای OpenSource ای که برای پژوهش‌ها و کارهای عملی خود استفاده خواهید کرد، به کرات توابع و کلاس‌های Generic را خواهید دید. بنابراین وظیفه دانستم تا یک بخش جداگانه برای این مفهوم در نظر بگیرم که اگر تا به الان با دیدن genericها حس وارد شدن به دنیای جدیدی به شما دست داده و باعث میشد یک قدم از پیش رفتن به سمت توسعه یک کنترل‌کننده مبتنی بر جاوا به عقب برگردید، از این پس دو قدم هم  جلوتر رفته و با آغوش باز از genericها استقبال کنید… چرا که نه؟!

 

تاریخچه آموزش جاوا به ترتیب:

قسمت اول آموزش جاوا (مقدمه)

قسمت دوم آموزش جاوا (Types and Variables)

قسمت سوم آموزش جاوا (دستورات شرطی)

قسمت چهارم آموزش جاوا (آرایه‌ها)

قسمت پنجم آموزش جاوا (حلقه‌ها)

قسمت ششم آموزش جاوا (توابع)

قسمت هفتم آموزش جاوا (اشیا)

قسمت هشتم آموزش جاوا (ارث‌بری)

قسمت نهم آموزش جاوا (try and catch)

قسمت دهم آموزش جاوا (کلاس‌های abstract)

قسمت یازدهم آموزش جاوا (Interfaces)

قسمت دوازدهم آموزش جاوا (مفهوم Generic)

قسمت سیزدهم آموزش جاوا (Networking)

قسمت چهاردهم آموزش جاوا (مفهوم نخ و رشته)

آموزش جاوا – قسمت 12 (مفهوم Generic)
امتیاز دهید

(2) دیدگاه

  • سارا پاسخ

    با تشکر از سایت خوبتون
    میتونم بپرسم شما به صورت حضوری هم یاد میدین یا نه؟
    یعنی آدرس یا دفتری دارین؟ من که پیدا نکردم

    ۲۷ آبان ۱۳۹۶ در ۶:۰۹ ب.ظ

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *