CodeGym /Blogue Java /Random-PT /Enum. Exemplos práticos. Adicionando construtores e métod...
John Squirrels
Nível 41
San Francisco

Enum. Exemplos práticos. Adicionando construtores e métodos

Publicado no grupo Random-PT
Oi! Hoje falaremos sobre um dos tipos de dados especiais de Java: Enum(abreviação de "enumeração"). O que o torna especial? Vamos imaginar o que precisamos para implementar "meses" em um programa. Enum.  Exemplos práticos.  Adicionando construtores e métodos - 1 Não parece problemático, certo? Só precisamos determinar quais propriedades cada mês possui. Talvez primeiro precisemos do nome do mês e do número de dias nele. A solução parece bem simples:

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 +
               '}';
   }
}
Todo o shabang! Temos uma Monthclasse, os campos obrigatórios, getter/setters e toString(). A menos, é claro, que precisemos adicionar equals()ehashCode()para alcançar a felicidade completa :) Mas aqui temos um problema conceitual. Como você provavelmente se lembra, uma das principais vantagens da OOP é que ela facilita a modelagem de entidades do mundo real. Uma cadeira, um carro, um planeta - todos esses conceitos da vida cotidiana são facilmente representados em um programa com a ajuda da abstração. O problema é que algumas entidades do mundo real têm uma faixa de valores estritamente limitada. Há apenas 4 estações em um ano. Existem apenas 8 notas em uma oitava. O calendário tem apenas 12 meses. E Danny Ocean of Ocean's 11 tem apenas 11 amigos (embora isso não importe :)) O que importa é que uma classe Java comum não é capaz de modelar essas entidades e impor suas limitações naturais. NossoMonthclass tem todos os campos obrigatórios. Mas se outro programador o usar, ninguém poderá impedi-lo de criar objetos completamente insanos:

public class Main {

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

}
Se isso aparecer em nosso código, não será fácil encontrar o culpado! Por um lado, o programador que cria os objetos pode perceber que a Monthclasse significa "mês em um ano" e não escrever essas bobagens. Por outro lado, o programador está apenas aproveitando as habilidades fornecidas pelo designer de classe. É possível atribuir nomes e números de dias arbitrários? Isso é exatamente o que temos. O que então devemos fazer nesta situação? Honestamente, antes do lançamento do Java 1.5, os programadores precisavam ser criativos :) Naquela época, eles criavam estruturas como esta:

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 +
               '}';
   }
}
Aqui reduzimos o número de meses de doze para três para tornar o exemplo mais curto. Tais projetos tornaram possível resolver o problema. A capacidade de criar objetos era limitada a um construtor privado:

private Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }
Os programadores que usam a classe não podem simplesmente criar Monthobjetos. Eles tiveram que usar os objetos estáticos finais fornecidos pelo desenvolvedor da classe. Por exemplo, assim:

public class Main {

   public static void main(String[] args) {

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

}
Mas os desenvolvedores Java chamaram a atenção para o problema existente. Claro, é ótimo que os programadores tenham conseguido encontrar uma solução usando as ferramentas disponíveis na linguagem, mas não parece muito fácil! Era necessária uma solução óbvia, mesmo para novatos. E assim Enumapareceu em Java. Basicamente, Enumé uma classe Java que fornece um conjunto limitado de valores de objeto. Veja como fica:

public enum Month {
  
   JANUARY,
   FEBRUARY,
   MARCH
}
Na definição, indicamos que Enumé uma classe Java, mas isso é realmente verdade? Sim, e podemos até verificar isso. Por exemplo, tente fazer nosso Monthenum herdar alguma outra classe:

public abstract class AbstractMonth {
}

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

   JANUARY,
   FEBRUARY,
   MARCH
}
Por que isso acontece? Quando escrevemos:

public enum Month
o compilador converte esta instrução no seguinte código:

public Class Month extends Enum
Como você já sabe, Java não suporta herança múltipla. Portanto, não podemos herdar AbstractMonth. Como essa nova construção, Enum, pode ser usada? E como isso difere da antiga construção com static finalcampos? Bem, por exemplo, a construção antiga não nos permitia usar nosso próprio conjunto de valores nas switchdeclarações. Imagine que queremos criar um programa que nos lembre dos feriados comemorados a cada mês:

public class HolidayReminder {

   public void printHolidays(Month month) {

       switch (month) {

           // Error!
           case JANUARY:
       }
   }
}
Como você pode ver, o compilador gera um erro aqui. Mas uma vez que enumapareceu no Java 1.5, tudo ficou muito mais simples:

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);

   }

}
Saída do console:

New Year's Day is January 1st!
Observe que o acesso aos Enumobjetos permaneceu estático, exatamente como era antes do Java 1.5. Não precisamos criar um Monthobjeto para acessar os meses. Ao trabalhar com enums, é muito importante não esquecer que Enumé uma classe completa. Isso significa que, se necessário, você pode definir construtores e métodos nele. Por exemplo, no fragmento de código anterior, simplesmente especificamos os valores: JANUARY, FEVEREIRO, MARÇO. No entanto, podemos expandir nosso Monthenum assim:

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 +
               '}';
   }
}
Aqui fornecemos nossos enum2 campos (o nome do mês e o número de dias), um construtor que usa esses campos, getter/setters, o toString()método e 2 métodos estáticos. Como você pode ver, não houve problemas com isso. Novamente, Enumrealmente é uma classe completa:

import java.util.Arrays;

public class Main {

   public static void main(String[] args) {

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

   }

}
Saída do console:

[Month{name='June', daysCount=30}, Month{name='July', daysCount=31}, Month{name='August', daysCount=31}]
Por fim, gostaria de recomendar um livro Java extremamente útil, chamado "Effective Java" de Joshua Bloch . Enum.  Exemplos práticos.  Adicionando construtores e métodos - 3O autor é um dos criadores do Java, então você pode confiar em seus conselhos sobre como usar as ferramentas da linguagem de maneira correta e competente :) Em relação à nossa lição, recomendo que você preste atenção especial ao capítulo do livro sobre Enum. Leitura feliz! :)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION