۳۰ آذر ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

نحوه استفاده از typesafe enums در جاوا

Typesafe enums جایگزین بهتری برای انواع برشماری سنتی جاوا است. در اینجا نحوه استفاده صحیح از typesafe enums در کد جاوا آورده شده است.

Typesafe enums جایگزین بهتری برای انواع برشماری سنتی جاوا است. در اینجا نحوه استفاده صحیح از typesafe enums در کد جاوا آورده شده است.

این مقاله شما را با تفاوت بین انواع شمارش شده و typeafe enum آشنا می کند. شما یاد خواهید گرفت که چگونه یک typeafe enum را اعلام کنید و از آن در یک دستور switch استفاده کنید و خواهید دید که چگونه با افزودن داده ها و رفتارها یک typesafe enum را سفارشی کنید. ما همچنین نگاهی به java.lang.Enum> خواهیم انداخت، که کلاس پایه برای همه typesafe enums است.

آنچه در این آموزش جاوا خواهید آموخت

  • چرا از typesafe enums استفاده کنید و از نوع های شمارش نشده استفاده کنید
  • نحوه استفاده از typesafe enums در دستورات سوئیچ
  • نحوه اضافه کردن داده ها و رفتارها به typesafe enums
  • جزئیات و نمونه‌های کلاس Enum (Enum>)

چرا از typesafe enums استفاده کنیم، نه از انواع برشماری

یک نوع شمارش شده مجموعه ای از ثابت های مرتبط را به عنوان مقادیر خود مشخص می کند. مثال‌ها شامل روزهای هفته، جهت‌های قطب‌نمای شمالی/جنوبی/شرق/غربی استاندارد، اسکناس سکه‌های یک ارز، و انواع رمز یک تحلیلگر واژگانی است.

انواع شمارش‌شده به‌طور سنتی به‌عنوان دنباله‌ای از ثابت‌های عدد صحیح پیاده‌سازی می‌شوند که با مجموعه‌ای از ثابت‌های جهت زیر نشان داده می‌شود:


static final int DIR_NORTH = 0;
static final int DIR_WEST = 1;
static final int DIR_EAST = 2;
static final int DIR_SOUTH = 3;

چندین مشکل در این رویکرد وجود دارد:

  • ایمنی نوع ندارد: از آنجا که یک ثابت نوع برشماری شده فقط یک عدد صحیح است، هر عدد صحیحی را می توان در جایی که ثابت مورد نیاز است مشخص کرد. علاوه بر این، جمع، تفریق و سایر عملیات ریاضی را می توان بر روی این ثابت ها انجام داد. به عنوان مثال، (DIR_NORTH + DIR_EAST) / DIR_SOUTH)، که بی معنی است.
  • فضای نام موجود نیست: ثابت های یک نوع شمارش شده باید با نوعی (امیدوارم) شناسه منحصر به فرد (به عنوان مثال، DIR_) پیشوند شوند تا از برخورد با دیگری جلوگیری شود. ثابت های نوع برشمرده شده است.
  • کد شکننده است: از آنجایی که ثابت‌های نوع برشماری شده در فایل‌های کلاس کامپایل می‌شوند، جایی که مقادیر تحت اللفظی آن‌ها (در مخزن‌های ثابت) ذخیره می‌شوند، تغییر مقدار ثابت مستلزم آن است که این فایل‌های کلاس و آن فایل‌های کلاس کاربردی که به آنها بستگی دارد بازسازی شوند. در غیر این صورت، رفتار تعریف نشده در زمان اجرا رخ می دهد.
  • اطلاعات کافی نیست: وقتی یک ثابت چاپ می شود، مقدار صحیح آن خروجی می شود. این خروجی چیزی در مورد اینکه مقدار عدد صحیح نشان می دهد به شما نمی گوید. حتی نوع برشماری شده ای که ثابت به آن تعلق دارد را مشخص نمی کند.

می توانید با استفاده از ثابت های java.lang.String از مشکلات “عدم ایمنی نوع” و “اطلاعات کافی” جلوگیری کنید. برای مثال، ممکن است رشته نهایی ثابت DIR_NORTH = "NORTH"؛ را مشخص کنید. اگرچه مقدار ثابت معنی‌دارتر است، اما ثابت‌های مبتنی بر String همچنان از مشکلات ” فضای نام موجود نیست ” و شکنندگی رنج می‌برند. همچنین، برخلاف مقایسه‌های عدد صحیح، نمی‌توانید مقادیر رشته را با عملگرهای == و != (که فقط مراجع را مقایسه می‌کنند) مقایسه کنید.

برای حل این مشکلات، توسعه دهندگان یک جایگزین مبتنی بر کلاس به نام typesafe enum اختراع کردند. متأسفانه، این الگو دارای چالش های متعددی بود. . من نمونه ای را نشان می دهم که مسائل را خلاصه می کند.

چالش های الگوی typesafe enum

کلاس Suit نشان می‌دهد که چگونه می‌توانید از الگوی typeafe enum برای معرفی یک نوع برشماری که چهار لباس کارت را توصیف می‌کند استفاده کنید:


public final class Suit // Should not be able to subclass Suit.
{
   public static final Suit CLUBS = new Suit();
   public static final Suit DIAMONDS = new Suit();
   public static final Suit HEARTS = new Suit();
   public static final Suit SPADES = new Suit();
   private Suit() {} // Should not be able to introduce additional constants.
}

برای استفاده از این کلاس، باید یک متغیر Suit معرفی کنید و آن را به یکی از ثابت‌های Suit اختصاص دهید:


Suit suit = Suit.DIAMONDS;

پس ممکن است بخواهید suit را در یک عبارت switch مانند این مورد بازجویی کنید:


switch (suit)
{
   case Suit.CLUBS   : System.out.println("clubs"); break;
   case Suit.DIAMONDS: System.out.println("diamonds"); break;
   case Suit.HEARTS  : System.out.println("hearts"); break;
   case Suit.SPADES  : System.out.println("spades");
}

با این حال، هنگامی که کامپایلر جاوا با Suit.CLUBS روبرو می شود، خطایی را گزارش می دهد که بیان می کند یک عبارت ثابت مورد نیاز است. ممکن است سعی کنید مشکل را به صورت زیر حل کنید:


switch (suit)
{
   case CLUBS   : System.out.println("clubs"); break;
   case DIAMONDS: System.out.println("diamonds"); break;
   case HEARTS  : System.out.println("hearts"); break;
   case SPADES  : System.out.println("spades");
}

اما هنگامی که کامپایلر با CLUBS روبرو می شود، خطایی را گزارش می دهد که نشان می دهد قادر به یافتن نماد نیست. و حتی اگر Suit را در یک بسته قرار دهید، بسته را وارد کنید، و این ثابت ها را به صورت ایستا وارد کنید، کامپایلر شکایت می کند که نمی تواند Suit را به int تبدیل کند. code> هنگام مواجهه با کت و شلوار در switch(suit). در مورد هر case، کامپایلر همچنین گزارش می‌دهد که یک عبارت ثابت مورد نیاز است.

جاوا از الگوی typesafe enum با دستورات switch پشتیبانی نمی کند. با این حال، می‌توانید از ویژگی زبان typesafe enum استفاده کنید، که مزایای الگو را در حین حل مشکلات آن در بر می‌گیرد. این ویژگی همچنین از switch پشتیبانی می کند.

نحوه استفاده از typesafe enums در دستورات سوئیچ

یک اعلان enum ساده Typeafe در کد جاوا شبیه همتایان خود در زبان‌های C، C++ و C# است:


enum Direction { NORTH, WEST, EAST, SOUTH }

این اعلان از کلمه کلیدی enum برای معرفی Direction به عنوان typeafe enum (نوع ویژه ای از کلاس) استفاده می کند، که در آن می توان روش های دلخواه را اضافه کرد و رابط های دلخواه را می توان اضافه کرد. اجرا شد. ثابت‌های NORTH، WEST، EAST و SOUTH enum به‌عنوان ثابت پیاده‌سازی می‌شوند. - بدنه‌های کلاس خاص که کلاس‌های ناشناس را تعریف می‌کنند و کلاس Direction را گسترش می‌دهند.

Direction و سایر enumهای typeafe Enum> را گسترش می‌دهند و متدهای مختلفی از جمله values()، < را به ارث می‌برند. code>toString() و compareTo()، از این کلاس. Enum را در ادامه این مقاله بررسی خواهیم کرد.

فهرست ۱ enum فوق الذکر را اعلام می کند و از آن در عبارت switch استفاده می کند. همچنین نحوه مقایسه دو ثابت enum را نشان می دهد تا مشخص شود کدام ثابت قبل از ثابت دیگر قرار می گیرد.


public class TEDemo
{
   enum Direction { NORTH, WEST, EAST, SOUTH }
   public static void main(String[] args)
   {
      for (int i = 0; i < Direction.values().length; i++)
      {
         Direction d = Direction.values()[i];
         System.out.println(d);
         switch (d)
         {
            case NORTH: System.out.println("Move north"); break;
            case WEST : System.out.println("Move west"); break;
            case EAST : System.out.println("Move east"); break;
            case SOUTH: System.out.println("Move south"); break;
            default   : assert false: "unknown direction";
         }
      }
      System.out.println(Direction.NORTH.compareTo(Direction.SOUTH));
   }
}

فهرست ۱ Direction typeafe enum را اعلام می کند و روی اعضای ثابت آن تکرار می شود که values() برمی گرداند. برای هر مقدار، عبارت switch (برای پشتیبانی از typeafe enums بهبود یافته است) case را که مطابق با مقدار  d است انتخاب می‌کند و یک پیام مناسب را خروجی می‌کند. . (شما یک پیشوند ثابت enum، به عنوان مثال NORTH را با نوع enum آن اضافه نمی کنید.) در نهایت، فهرست ۱ Direction.NORTH.compareTo(Direction.SOUTH) را به ارزیابی می کند. تعیین کنید که آیا NORTH قبل از SOUTH آمده است یا خیر.

کد منبع را به صورت زیر کامپایل کنید:

javac TEDemo.java

برنامه کامپایل شده را به صورت زیر اجرا کنید:

java TEDemo

شما باید خروجی زیر را مشاهده کنید:

NORTH
Move north
WEST
Move west
EAST
Move east
SOUTH
Move south
-۳

خروجی نشان می‌دهد که روش ارثی toString() نام ثابت enum را برمی‌گرداند و NORTH قبل از SOUTH در یک می‌آید. مقایسه این ثابت های enum.

نحوه اضافه کردن داده ها و رفتارها در typesafe enums

می توانید داده ها (به شکل فیلدها) و رفتارها (به شکل متدها) را به یک typeafe enum اضافه کنید. برای مثال، فرض کنید باید یک عدد برای سکه‌های کانادایی معرفی کنید، و می‌خواهید کلاس ابزاری برای برگرداندن تعداد نیکل، سکه، ربع یا دلار موجود در تعداد دلخواه پنی فراهم کند. فهرست ۲ نحوه انجام این کار را به شما نشان می دهد.


enum Coin
{
   NICKEL(5),   // constants must appear first
   DIME(10),
   QUARTER(25),
   DOLLAR(100); // the semicolon is required
   private final int valueInPennies;
   Coin(int valueInPennies)
   {
      this.valueInPennies = valueInPennies;
   }
   int toCoins(int pennies)
   {
      return pennies / valueInPennies;
   }
}
public class TEDemo
{
   public static void main(String[] args)
   {
      if (args.length != 1)
      {
          System.err.println("usage: java TEDemo amountInPennies");
          return;
      }
      int pennies = Integer.parseInt(args[0]);
      for (int i = 0; i < Coin.values().length; i++)
           System.out.println(pennies + " pennies contains " +
                              Coin.values()[i].toCoins(pennies) + " " +
                              Coin.values()[i].toString().toLowerCase() + "s");
   }
}

فهرست ۲ ابتدا یک Coin را اعلام می کند. لیستی از ثابت های پارامتر شده چهار نوع سکه را مشخص می کند. آرگومان ارسال شده به هر ثابت نشان دهنده تعداد پنی هایی است که سکه نشان می دهد.

آگومان ارسال شده به هر ثابت به سازنده Coin(int valueInPennies) ارسال می شود، که آرگومان را در قسمت نمونه valuesInPennies ذخیره می کند. این متغیر از طریق روش نمونه toCoins() قابل دسترسی است. به تعداد سکه‌های ارسال شده به پارامتر pennies toCoin() تقسیم می‌شود، و این روش نتیجه را برمی‌گرداند که اتفاقاً تعداد سکه‌های موجود در نام پولی است. توسط ثابت Coin توصیف شده است.

در این مرحله، متوجه شده‌اید که می‌توانید فیلدهای نمونه، سازنده‌ها و متدهای نمونه را در یک typeafe enum اعلام کنید. به هر حال، typesafe enum اساسا نوع خاصی از کلاس جاوا است.

روش main() کلاس TEDemo ابتدا تأیید می‌کند که یک آرگومان خط فرمان منفرد مشخص شده است. این آرگومان با فراخوانی متد parseInt() کلاس java.lang.Integer به عدد صحیح تبدیل می‌شود، که مقدار آرگومان رشته‌ای خود را به یک عدد صحیح تجزیه می‌کند (یا یک عدد می‌اندازد). استثنا زمانی که ورودی نامعتبر شناسایی شود).

حرکت به جلو، main() روی ثابت‌های Coin تکرار می‌شود. از آنجایی که این ثابت ها در یک آرایه Coin[] ذخیره می شوند، main() Coin.values().length را ارزیابی می کند تا طول آن را تعیین کند. آرایه. برای هر تکرار از فهرست حلقه i، main() Coin.values()[i] را ارزیابی می کند تا به Coin ثابت. هر یک از toCoins() و toString() را روی این ثابت فراخوانی می کند، که بیشتر ثابت می کند که Coin نوع خاصی از کلاس است. p>

کد منبع را به صورت زیر کامپایل کنید:

javac TEDemo.java

برنامه کامپایل شده را به صورت زیر اجرا کنید:

java TEDemo ۱۹۸

شما باید خروجی زیر را مشاهده کنید:


۱۹۸ pennies contains 39 nickels
۱۹۸ pennies contains 19 dimes
۱۹۸ pennies contains 7 quarters
۱۹۸ pennies contains 1 dollars

آنچه باید در مورد Enum بدانید (Enum>)

کامپایلر جاوا enum را قند نحوی در نظر می گیرد. پس از مواجهه با اعلان enum typeafe، کلاسی تولید می کند که نام آن توسط اعلان مشخص شده است. این کلاس، کلاس انتزاعی Enum> را که به عنوان کلاس پایه برای همه typesafe enum ها عمل می کند، قرار می دهد.

فهرست پارامترهای نوع رسمی

Enum وحشتناک به نظر می رسد، اما درک آن چندان سخت نیست. به عنوان مثال، در زمینه Coin extensions Enum، شما می توانید این لیست پارامتر نوع رسمی را به صورت زیر تفسیر کنید:

  • هر زیر کلاس Enum باید یک آرگومان نوع واقعی را برای Enum ارائه کند. برای مثال، سربرگ Coin Enum را مشخص می‌کند.
  • آگومان نوع واقعی باید زیر کلاس Enum باشد. به عنوان مثال، Coin یک زیر کلاس از Enum است.
  • یک زیر کلاس از Enum (مانند Coin) باید از اصطلاحی پیروی کند که نام خود (Coin) را به عنوان یک نوع واقعی ارائه می‌کند. استدلال.

مستندات جاوا Enum را بررسی کنید و متوجه خواهید شد که java.lang.Object clone() را لغو می کند، < کد>برابر ()، finalize()، hashCode() و toString() متدهای است. به جز toString()، همه این متدهای نادیده گرفته شده نهایی اعلام می شوند تا در یک زیر کلاس قابل بازنویسی نباشند:

  • clone() برای جلوگیری از شبیه‌سازی ثابت‌ها لغو می‌شود تا هرگز بیش از یک کپی از یک ثابت وجود نداشته باشد. در غیر این صورت، ثابت ها را نمی توان از طریق == و != مقایسه کرد.
  • equals() برای مقایسه ثابت ها از طریق مراجع آنها لغو می شود. ثابت‌های با هویت‌های یکسان (==) باید محتویات یکسانی داشته باشند (equals())، و هویت‌های مختلف حاکی از محتویات متفاوتی است.
  • finalize() لغو می شود تا اطمینان حاصل شود که ثابت ها نمی توانند نهایی شوند.
  • hashCode() لغو شده است زیرا equals() لغو شده است.
  • toString() برای برگرداندن نام ثابت لغو می شود.

Enum نیز روش های خاص خود را ارائه می دهد. این روش‌ها عبارتند از نهایی compareTo() (Enum رابط java.lang.Comparable را پیاده‌سازی می‌کند) روش‌های getDeclaringClass()، name() و ordinal():

شاید به این مطالب علاقمند باشید