Java Enum Methods - zwraca wyliczenie w przeciwnym kierunku

113

Chciałbym zadeklarować kierunek wyliczenia, który ma metodę zwracającą przeciwny kierunek (poniższy kod nie jest poprawny składniowo, tj. Nie można utworzyć instancji wyliczeń, ale ilustruje to mój punkt widzenia). Czy to możliwe w Javie?

Oto kod:

public enum Direction {

     NORTH(1),
     SOUTH(-1),
     EAST(-2),
     WEST(2);

     Direction(int code){
          this.code=code;
     }
     protected int code;
     public int getCode() {
           return this.code;
     }
     static Direction getOppositeDirection(Direction d){
           return new Direction(d.getCode() * -1);
     }
}
Skyler
źródło
Po prostu użyj przełącznika. Masz tylko 4 przypadki.
Sotirios Delimanolis
12
Nawiasem mówiąc, d.getCode() * -1==-d.getCode()
tckmn
Rozdział 6 (przynajmniej w 2E) Efektywnej Javy Blocha może być interesujący i wysoce zalecany.
jedwards
ntu.edu.sg/home/ehchua/programming/java/javaenum.html , sekcja 2.1 starannie wyjaśniła koncepcję
vikramvi

Odpowiedzi:

207

Dla tych, zwabionych tutaj tytułem: tak, możesz zdefiniować własne metody w swoim enum. Jeśli zastanawiasz się, jak wywołać taką niestatyczną metodę, robisz to tak samo, jak każdą inną niestatyczną metodę - wywołujesz ją na instancji typu, która definiuje lub dziedziczy tę metodę. W przypadku wyliczeń takie przypadki są po prostu ENUM_CONSTANTs.

Więc wszystko, czego potrzebujesz, to EnumType.ENUM_CONSTANT.methodName(arguments).


Wróćmy teraz do problemu z pytania. Jednym z rozwiązań mogłoby być

public enum Direction {

    NORTH, SOUTH, EAST, WEST;

    private Direction opposite;

    static {
        NORTH.opposite = SOUTH;
        SOUTH.opposite = NORTH;
        EAST.opposite = WEST;
        WEST.opposite = EAST;
    }

    public Direction getOppositeDirection() {
        return opposite;
    }

}

Teraz Direction.NORTH.getOppositeDirection()wróci Direction.SOUTH.


Oto trochę bardziej "hacky" sposób zilustrowania komentarza @jedwards, ale nie wydaje się on tak elastyczny jak pierwsze podejście, ponieważ dodanie kolejnych pól lub zmiana ich kolejności zepsuje nasz kod.

public enum Direction {
    NORTH, EAST, SOUTH, WEST;

    // cached values to avoid recreating such array each time method is called
    private static final Direction[] VALUES = values();

    public Direction getOppositeDirection() {
        return VALUES[(ordinal() + 2) % 4]; 
    }
}
Pshemo
źródło
3
Miałem zamiar .values()[ordinal()]włamać się, ale to podejście jest znacznie bardziej niezawodne
jedwards
jak z tego korzystasz? i jak nazywa się ta technika?
Thufir,
1
@Thufir " jak tego używasz " jeśli pytasz o metodę, to jak każda inna metoda - wywołujesz ją na instancji klasy tą metodą. Instancje Directionklasy enum są NORTH, EAST, SOUTH, WESTwięc można po prostu użyć NORTH.getOppositeDirection()i powróci SOUTH. „ jak nazywa się ta technika? ” jeśli o to pytasz, static{...}to jest to statyczny blok inicjalizacyjny , kod wywoływany przy pierwszym ładowaniu klasy (jest to część tego samego procesu, który inicjalizuje pola statyczne).
Pshemo,
@Pshemo, zastanawiam się, jak będzie wyglądała wersja Spring powyższego kodu, jeśli wartości, które są ustawiane w bloku statycznym, muszą zostać wstrzyknięte z pliku właściwości.
Vikas Prasad
161

W przypadku małego wyliczenia, takiego jak ten, najbardziej czytelnym rozwiązaniem jest:

public enum Direction {

    NORTH {
        @Override
        public Direction getOppositeDirection() {
            return SOUTH;
        }
    }, 
    SOUTH {
        @Override
        public Direction getOppositeDirection() {
            return NORTH;
        }
    },
    EAST {
        @Override
        public Direction getOppositeDirection() {
            return WEST;
        }
    },
    WEST {
        @Override
        public Direction getOppositeDirection() {
            return EAST;
        }
    };


    public abstract Direction getOppositeDirection();

}
Amir Afghani
źródło
8
Świetny pomysł! Jest to również dobre, gdy ogólnie chcesz, aby każda wartość wyliczenia miała określone zachowanie.
OferBr
28

To działa:

public enum Direction {
    NORTH, SOUTH, EAST, WEST;

    public Direction oppose() {
        switch(this) {
            case NORTH: return SOUTH;
            case SOUTH: return NORTH;
            case EAST:  return WEST;
            case WEST:  return EAST;
        }
        throw new RuntimeException("Case not implemented");
    }
}
Pikrass
źródło
8
Zamiast zwracać wartość null klauzuli domyślnej, która zgłasza odpowiedni wyjątek RuntimeException, może być lepszym wskazaniem, że wystąpił błąd programisty polegający na tym, że nie zdefiniowano przeciwieństwa dla nowo dodanego kierunku.
Timothy055
1
Wymaga to od wywołującego obsługi wartości null. Wymaga również od opiekuna upewnienia się, że dodaje przypadek za każdym razem, gdy dodawany jest nowy typ kierunku. Zobacz odpowiedź Amira Afghaniego na temat korzystania z metody abstrakcyjnej, którą można przesłonić każdą wartością wyliczenia, dzięki czemu nigdy nie ryzykujesz pominięcia jednej i nie musisz się martwić o obsługę wartości null.
Michael Peterson
14

Utwórz metodę abstrakcyjną i zastąp ją każdą z wartości wyliczenia. Ponieważ podczas tworzenia wiesz, że jest odwrotnie, nie ma potrzeby dynamicznego generowania ani tworzenia.

Nie czyta się jednak ładnie; może switchbyłoby łatwiejsze w zarządzaniu?

public enum Direction {
    NORTH(1) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.SOUTH;
        }
    },
    SOUTH(-1) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.NORTH;
        }
    },
    EAST(-2) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.WEST;
        }
    },
    WEST(2) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.EAST;
        }
    };

    Direction(int code){
        this.code=code;
    }
    protected int code;

    public int getCode() {
        return this.code;
    }

    public abstract Direction getOppositeDirection();
}
Makoto
źródło
4

Tak, robimy to cały czas. Zwracasz instancję statyczną zamiast nowego obiektu

 static Direction getOppositeDirection(Direction d){
       Direction result = null;
       if (d != null){
           int newCode = -d.getCode();
           for (Direction direction : Direction.values()){
               if (d.getCode() == newCode){
                   result = direction;
               }
           }
       }
       return result;
 }
BevynQ
źródło
0
public enum Direction {
    NORTH, EAST, SOUTH, WEST;

    public Direction getOppositeDirection(){
        return Direction.values()[(this.ordinal() + 2) % 4];
    }
}

Wyliczenia mają statyczną metodę wartości, która zwraca tablicę zawierającą wszystkie wartości wyliczenia w kolejności, w jakiej są zadeklarowane. źródło

ponieważ NORTH dostaje 1, EAST dostaje 2, SOUTH dostaje 3, WEST dostaje 4; możesz utworzyć proste równanie, aby otrzymać odwrotne:

(wartość + 2)% 4

Pregunton
źródło
2
dlaczego to jest odpowiedź? Jak myślisz, że pomoże to przyszłym czytelnikom lub komukolwiek w nauce bez żadnego wyjaśnienia?
GrumpyCrouton
Chociaż ten kod może odpowiedzieć na pytanie, dostarczenie dodatkowego kontekstu dotyczącego tego, jak i / lub dlaczego rozwiązuje problem, poprawiłoby długoterminową wartość odpowiedzi. Pamiętaj, że odpowiadasz na pytanie do czytelników w przyszłości, a nie tylko osoba, która zapyta teraz! Zmień swoją odpowiedź, dodając wyjaśnienie i wskaż, jakie ograniczenia i założenia mają zastosowanie. Nie zaszkodzi również wspomnieć, dlaczego ta odpowiedź jest bardziej odpowiednia niż inne.
ItamarG3
czy ciężko jest czytać kod bez komentarzy? czy potrzebujesz javadoc ekskluzywnego dla 7 linii kodu?
Pregunton
1
To rozwiązanie jest kruche, ponieważ zależy od kolejności wartości wyliczeniowych. Gdybyś miał zmienić kolejność na alfabetyczną, twoje sprytne równanie nie zapewniałoby już właściwego odwrotności.
Josh J,