Java EnumUżywanie typu wyliczeniowego redukuje w kodzie pewne nieścisłości. Przykładowo sklep z gitarami może przechowywać wartości określające typy gitar jako napis string. Problem polega na tym ,że do typu string można  umieścić każdy napis. Tworzenie takiego modelu działania dla jakiegokolwiek sklepu stworzyłoby ogromny problem ponieważ system ten byłby podatny na różnego rodzaju błędy.

Do takich zadań w językach obiektowych jest typ wyliczeniowy (enum). Typ wyliczeniowy przyjmuje tylko określone wartości. Oto przykład enum z typami gitar.

Kończąc ten wpis zdałem sobie sprawę ,że ten typ ma wartości oznaczające składowe gitary , a nie typ gitary. Proszę to zignorować.

enum GuitarType
{
    General,
    Headstock,
    Fretboard,
    Frets,
    Inlays,
    Neck,
    Heel,
};

Teraz zmienna typu “GuitarType” będzie mogła przyjmować tylko takie wartości.

GuitarType gt = GuitarType.Heel;

Nie jest to wymagane ,ale stałe w typach enum powinny być napisane z dużej litery.

enum GuitarType
{
    GENERAL,
    HEADSTOCK,
    FRETBOARD,
    FRETS,
    INLAYS,
    NECK,
    HEEL,
};

Skoro dokumentacja Sun ma taką konwencje może nie jest to taki zły pomysł. Na egzaminie może to być istotna sprawa.

Podstawowe komponenty enum są stałe jak (GENERAL, HEADSTOCK) jednak typ wyliczeniowy zawiera  więcej wartości niż się może wydawać na początku.

Enum może być deklarowany jakby  był oddzielną klasą ale też może   być elementem klasy. Typ wyliczeniowy nigdy nie może być  zadeklarowany wewnątrz metody.

Deklarowanie enum poza klasą i jej użycie w klasie.

package dPaczka;

enum GuitarType{GENERAL,HEADSTOCK,
    FRETBOARD,FRETS,INLAYS,NECK,HEEL,};

class Guitar
{
    GuitarType type = GuitarType.INLAYS;
}    
    
public class GuitarTest 
{
    void test()
    {
        Guitar guitar = new Guitar();
        guitar.type = GuitarType.FRETS;
    }
}

Ten kod znajduje się wewnątrz jednego pliku (plik musi być nazwany po klasie publicznej GuitarTest.java). Kluczową cechą w tym przykładzie jest fakt ,że temu typowi wyliczeniowemu można nadać tylko dwa modyfikatory dostępu public i default. ponieważ  nie jest zadeklarowany wewnątrz żadnej klasy.

Oto przykład deklaracji enum wewnątrz klasy.

package dPaczka;

class Guitar
{
    
    enum GuitarType2{GENERAL,HEADSTOCK,
        FRETBOARD,FRETS,INLAYS,NECK,HEEL,};
        
    GuitarType2 type = GuitarType2.INLAYS;
}    

public class GuitarTest2 
{
    void testIt()
    {
        
        Guitar g = new Guitar();
        g.type = Guitar.GuitarType2.NECK;
        //wymagane odwołanie się do klasy 
    }
}

W tym przykładzie widać ,że jeśli typ wyliczeniowy został zadeklarowany w klasie trzeba się najpierw do niej odwołać by go użyć. W końcu jest on elementem tej klasy. Tak jak pisałem wcześniej typ wyliczeniowy wewnątrz klasy może być prywatny bądź chroniony.

Nie da się zadeklarować typu wyliczeniowego wewnątrz metody.

image

Pułapką na egzaminie może być pewien drobny szczegół na temat typu wyliczeniowego. Otóż w czasie deklaracji po nawiasie klamrowym myślnik “;” jest opcjonalny. Co oznacza ,że oba zapisy są poprawne.

enum GuitarType4{GENERAL,HEADSTOCK,
    FRETBOARD,FRETS,INLAYS,NECK,HEEL,}

enum GuitarType3{GENERAL,HEADSTOCK,
    FRETBOARD,FRETS,INLAYS,NECK,HEEL,};

Co jest tworzone, gdy tworzysz enum. Typ wartościowy nie jest stringiem czy int-em. Każdy typ GuitarType jest faktyczną instancją GuitarType. Czyli to “GENERAL” jest typem GuitarType. O typie wyliczeniowym można pomyśleć jako o klasie ,ale dokładnie  to nie jest tak.

public class GuitarType 
{
    
    public static final GuitarType GENERAL =
                            new GuitarType("GENERAL",0);

    public static final GuitarType HEADSTOCK =
                            new GuitarType("HEADSTOCK",1);

    public static final GuitarType FRETBOARD =
                            new GuitarType("FRETBOARD",2);
    
    public static final GuitarType FRETS =
                            new GuitarType("FRETS",3);
    
    public static final GuitarType INLAYS =
                            new GuitarType("INLAYS",3);
            
    public static final GuitarType NECK =
                            new GuitarType("NECK",3);
    
    public static final GuitarType HEEL =
                            new GuitarType("HEEL",3);
    
    public GuitarType(String enumName,int index)
    {
        //kod 
    }
}

Zauważ jak wartości GENERAL,FRETBOARD są instancjami typu GuitarType. Wartości są reprezentowane przez dwa modyfikatory static i final co w Javie reprezentuje stałą. Zauważ także ,że każda wartość ma swój indeks pozycji co oznacza ,że kolejność deklarowanie wartości ma znaczenie.

Możesz pomyśleć o “GuitarType” jako o istniejącej tablicy, w której są przechowywane wszystkie wartości typu wyliczeniowego. Później pokażę jak w pętli można iterować każdą wartość w typie wyliczeniowym wywołując metodę values() w każdym typie wyliczeniowym.

Deklarowanie konstruktorów,metod i zmiennych wewnątrz typu wyliczeniowego

Ponieważ typ wyliczeniowy to specjalna klasa możesz w niej zrobić dużo więcej niż zadeklarować listę wartości. Możesz dodać do niej konstruktor, instancje zmiennych, metody i coś ciekawego, co jest znane jako “constant specific body”.

Pytanie brzmi dlaczego chciałbyś zadeklarować to wszystko. Aby to zrozumieć pomyśl o scenariuszu, w którym musisz znać inne informacje na temat konkretnego typu gitary jak np. rozmiar. Powiedzmy ,że dla GENERAL jest to 10 ,a dla NUT jest to 1. Logiczny sens tego scenariusza jest mały zwłaszcza ,że zdałem sobie sprawę ,że wartości te reprezentują części gitary ,a nie ich typy ale za dużo byłoby do poprawy w tym wpisie.

Mógłbyś stworzyć tabele czy inną strukturę danych do przechowywania takiej informacji ,ale byłoby to prawie niemożliwe do utrzymania. Najprostszy sposób polega na potraktowaniu wartości typu wyliczeniowego jako obiektu, który przechowuje swoje własne instancje zmiennych.

Te wartości będą przypisywane w czasie inicjowania typu wyliczeniowego, co oznacza ,że muszą być przekazane w   konstruktorze. Wydaje się to skomplikowane dlatego czas na przykład w kodzie.

package dPaczka;

enum GuitarType 
{
    //liczby 6,8,4 są przesyłane za pomocą konstruktora 
    ACOUSTIC(6),CLASSIC(8),FLAMENCO(4);
    
    GuitarType(int size)
    {
        //konstruktor this.size = size;
    }

    private int size;
    
    public int getSize(){
        return size;
    }
}

class Guitar 
{
    GuitarType type;
}

public class TestGuitar
{
    void testIT()
    {
        Guitar g1 = new Guitar();
        g1.type = GuitarType.FLAMENCO;
        
        Guitar g2 = new Guitar();
        g2.type = GuitarType.CLASSIC;
        
        System.out.print(g1.type.getSize());
        System.out.print(g2.type.getSize());
        
        for(GuitarType gt: GuitarType.values())
            System.out.print(gt + " = " + gt.getSize());
    }
}


Ten kod powinien  wyświetlić:

4
8
ACCOUSTIC = 6
FLAMENCO = 4
CLASSIC = 8

Każdy enum ma statyczną metodę “values()”, która zwraca tablicę wartości zawartą w tym enum w odpowiedniej kolejności.

Co trzeba wiedzieć na temat konstruktorów enum:

    • Nie możesz wywołać konstruktora typu wyliczeniowego bezpośrednio. Konstruktor ten jest wywoływany automatycznie z argumentami, które podałeś po wartości stałej. Na przykład dla wartości FLAMENCO (8) konstruktor pobiera int , którego wartość jest 8.
    • Możesz zdefiniować więcej niż jeden argument w konstruktorze, możesz też stworzyć więcej konstruktorów przeciążając go.

Nadszedł czas na ostatnie informacje w tym wpisie. Chodzi tutaj o pewne zachowanie w typie wyliczeniowym, które przypomina wewnętrzną klasę anonimową. Jest to znane jako “constant specific body”.używa się tego, gdy trzeba nadpisać metodę zdefiniowaną wewnątrz typu wyliczeniowego.

Przykładowo chcę przeciążać pewną metodę tylko dla specyficznej wartości typu wyliczeniowego.

W kodzie ta metoda nazwałaby się “getMoreInfo()” , która normalnie  zawracałaby napis “Informacje” ,ale dla wartości “FLAMENCO” metoda ta byłaby przeciążona i zwracałaby coś innego. Nie ma tutaj mowy o żadnych warunkach if i else wykonanie tego zadania w kodzie wygląda tak:

enum GuitarType 
{
    ACOUSTIC(6),
    CLASSIC(8),
    FLAMENCO(4)
    {
        public String getMoreInfo()
        {
            return "coś innego";
        }
    };

    //myślnik jest wymagany gdy istnieje dalsza 
    //część kodu     
    GuitarType(int size)
    {
        this.size = size;
    }

    private int size;
    
    public int getSize(){
        return size;
    }
    
    public String getMoreInfo()
    {
        return "Informacje";
    }
}

Co dalej:

Hermetyzacja.

Spis treści: