Hej! I dag vil vi tale om en af ​​Javas specielle datatyper: Enum(forkortelse for "enumeration"). Hvad gør det specielt? Lad os forestille os, hvad vi skal bruge for at implementere "måneder" i et program. Enum.  Praktiske eksempler.  Tilføjelse af konstruktører og metoder - 1 Det virker ikke problematisk, vel? Vi skal bare finde ud af, hvilke egenskaber den ene måned har. Måske har vi først brug for månedens navn og antallet af dage i den. Løsningen ser ret simpel ud:

public class Month {

   private String name;
   private int daysCount;

   public Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getDaysCount() {
       return daysCount;
   }

   public void setDaysCount(int daysCount) {
       this.daysCount = daysCount;
   }

   @Override
   public String toString() {
       return "Month{" +
               "name='" + name + '\'' +
               ", daysCount=" + daysCount +
               '}';
   }
}
Hele shabang! Vi har en Monthklasse, de obligatoriske felter, getter/settere og toString(). Medmindre vi selvfølgelig skal tilføje equals()oghashCode()at opnå fuldstændig lykke :) Men her har vi et konceptuelt problem. Som du sikkert husker, er en af ​​de vigtigste fordele ved OOP, at det gør det nemt at modellere enheder fra den virkelige verden. En stol, en bil, en planet - alle disse begreber fra det almindelige liv er let repræsenteret i et program ved hjælp af abstraktion. Problemet er, at nogle enheder i den virkelige verden har en strengt begrænset række af værdier. Der er kun 4 sæsoner på et år. Der er kun 8 toner i en oktav. Kalenderen har kun 12 måneder. Og Danny Ocean of Ocean's 11 har kun 11 venner (selvom det ikke betyder noget :)) Det der betyder noget er, at en almindelig Java-klasse ikke er i stand til at modellere disse entiteter og håndhæve deres naturlige begrænsninger. VoresMonthklasse har alle de obligatoriske felter. Men hvis en anden programmør bruger det, kan ingen forhindre ham eller hende i at skabe helt vanvittige objekter:

public class Main {

   Month month1 = new Month("lolkek", 322);
   Month month2 = new Month("yahoooooooooooo", 12345);

}
Hvis dette vises i vores kode, vil det ikke være nemt at finde den skyldige! På den ene side kan programmøren, der laver objekterne, indse, at klassen Monthbetyder "måned om et år" og ikke skrive sådan noget sludder. På den anden side udnytter programmøren kun de evner, som klassedesigneren gav. Er det muligt at tildele vilkårlige navne og antal dage? Det er præcis, hvad vi fik. Hvad skal vi så gøre i denne situation? Helt ærligt, før Java 1.5 blev frigivet, var programmører nødt til at være kreative :) I de dage skabte de strukturer som denne:

public class Month {

   private String name;
   private int daysCount;

   private Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }

   public static Month JANUARY = new Month("January", 31);
   public static Month FEBRUARY = new Month("February", 28);
   public static Month MARCH = new Month("March", 31);

   @Override
   public String toString() {
       return "Month{" +
               "name='" + name + '\'' +
               ", daysCount=" + daysCount +
               '}';
   }
}
Her har vi skåret antallet af måneder fra tolv til tre for at gøre eksemplet kortere. Sådanne designs gjorde det muligt at løse problemet. Muligheden for at skabe objekter var begrænset til en privat konstruktør:

private Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }
Programmører, der bruger klassen, kunne ikke bare oprette Monthobjekter. De skulle bruge de endelige statiske objekter leveret af klasseudvikleren. For eksempel sådan her:

public class Main {

   public static void main(String[] args) {

       Month january = Month.JANUARY;
       System.out.println(january);
   }

}
Men Java-udviklere henledte opmærksomheden på det eksisterende problem. Det er selvfølgelig fantastisk, at programmører kunne finde på en løsning ved hjælp af de værktøjer, der findes på sproget, men det ser ikke særlig nemt ud! En åbenlys løsning var nødvendig, selv for nybegyndere. Og så Enumoptrådte i Java. Dybest set Enumer det en Java-klasse, der giver et begrænset sæt objektværdier. Sådan ser det ud:

public enum Month {
  
   JANUARY,
   FEBRUARY,
   MARCH
}
I definitionen angav vi, at det Enumer en Java-klasse, men er det virkelig sandt? Ja, og vi kan endda bekræfte det. Prøv for eksempel at få vores Monthenum til at arve en anden klasse:

public abstract class AbstractMonth {
}

// Error! The extends clause cannot be used with an enum
public enum Month extends AbstractMonth {

   JANUARY,
   FEBRUARY,
   MARCH
}
Hvorfor sker det? Når vi skriver:

public enum Month
compileren konverterer denne sætning til følgende kode:

public Class Month extends Enum
Som du allerede ved, understøtter Java ikke multipel nedarvning. Derfor kan vi ikke arve AbstractMonth. Hvordan kan denne nye konstruktion, Enum, bruges? Og hvordan adskiller den sig fra den gamle konstruktion med static finalmarker? Tja, som et eksempel tillod den gamle konstruktion os ikke at bruge vores eget sæt værdier i switchudsagn. Forestil dig, at vi ønsker at skabe et program, der minder os om de højtider, der fejres hver måned:

public class HolidayReminder {

   public void printHolidays(Month month) {

       switch (month) {

           // Error!
           case JANUARY:
       }
   }
}
Som du kan se, kaster compileren en fejl her. Men en gang enumdukkede op i Java 1.5, blev alt meget enklere:

public enum Month {

   JANUARY,
   FEBRUARY,
   MARCH
}

public class HolidayReminder {

   public void printHolidays(Month month) {

       switch (month) {
          
           case JANUARY:
               System.out.println("New Year's Day is January 1st!");
               break;
           case FEBRUARY:
               System.out.println("Valentine's Day is February 14th!");
               break;
           case MARCH:
               System.out.println("Saint Patrick's Day is March 17th!");
               break;
       }
   }
}


public class Main {

   public static void main(String[] args) {

       HolidayReminder reminder = new HolidayReminder();
       reminder.printHolidays(Month.JANUARY);

   }

}
Konsoludgang:

New Year's Day is January 1st!
Bemærk, at adgangen til Enumobjekter forblev statisk, ligesom den var før Java 1.5. Vi behøver ikke oprette et Monthobjekt for at få adgang til månederne. Når du arbejder med enums, er det meget vigtigt ikke at glemme, at det Enumer en fuldgyldig klasse. Det betyder, at du om nødvendigt kan definere konstruktører og metoder i den. For eksempel, i det forrige kodefragment specificerede vi blot værdierne: JANUAR, FEBRUAR, MARTS. Vi kan dog udvide vores Monthenum sådan her:

public enum Month {

   JANUARY("January", 31),
   FEBRUARY("February", 28),
   MARCH("March", 31),
   APRIL("April", 30),
   MAY("May", 31),
   JUNE("June", 30),
   JULY("July", 31),
   AUGUST("August", 31),
   SEPTEMBER("September", 30),
   OCTOBER("October", 31),
   NOVEMBER("November", 30),
   DECEMBER("December", 31);

   private String name;
   private int daysCount;

   Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }

   public static Month[] getWinterMonths() {

       return new Month[]{DECEMBER, JANUARY, FEBRUARY};
   }

   public static Month[] getSummerMonths() {

       return new Month[]{JUNE, JULY, AUGUST};
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getDaysCount() {
       return daysCount;
   }

   public void setDaysCount(int daysCount) {
       this.daysCount = daysCount;
   }

   @Override
   public String toString() {
       return "Month{" +
               "name='" + name + '\'' +
               ", daysCount=" + daysCount +
               '}';
   }
}
Her gav vi vores enum2 felter (navnet på måneden og antallet af dage), en konstruktør, der bruger disse felter, getter/settere, metoden toString()og 2 statiske metoder. Som du kan se, var der ingen problemer med dette. Igen, Enumer virkelig en fuldgyldig klasse:

import java.util.Arrays;

public class Main {

   public static void main(String[] args) {

       System.out.println(Arrays.toString(Month.getSummerMonths()));

   }

}
Konsoludgang:

[Month{name='June', daysCount=30}, Month{name='July', daysCount=31}, Month{name='August', daysCount=31}]
Til sidst vil jeg anbefale en yderst brugbar Java-bog, nemlig "Effektiv Java" af Joshua Bloch . Enum.  Praktiske eksempler.  Tilføjelse af konstruktører og metoder - 3Forfatteren er en af ​​Javas skabere, så du kan helt sikkert stole på hans råd om, hvordan du korrekt og kompetent bruger sprogets værktøjer :) I forhold til vores lektion anbefaler jeg, at du er særlig opmærksom på bogens kapitel om Enum. God læselyst! :)