Jako programiści Javy projektujący niektóre interfejsy API często napotykamy ten problem. Byłem potwierdzając moje własne wątpliwości, gdy natknąłem się na ten post, ale mam opisowy obejście do niego:
// class name is awful for this example, but it will make more sense if you
// read further
public interface MetaDataKey<T extends Serializable> extends Serializable
{
T getValue();
}
public final class TypeSafeKeys
{
static enum StringKeys implements MetaDataKey<String>
{
A1("key1");
private final String value;
StringKeys(String value) { this.value = value; }
@Override
public String getValue() { return value; }
}
static enum IntegerKeys implements MetaDataKey<Integer>
{
A2(0);
private final Integer value;
IntegerKeys (Integer value) { this.value = value; }
@Override
public Integer getValue() { return value; }
}
public static final MetaDataKey<String> A1 = StringKeys.A1;
public static final MetaDataKey<Integer> A2 = IntegerKeys.A2;
}
w tym momencie, można uzyskać korzyści z bycia prawdziwie stały enum
wartość chłodnika (i cały korzyści, które pasują do tego), a także jest unikalną implementacją interface
, ale masz globalną dostępność pożądaną przez enum
.
Dodaje to wyraźnie gadatliwości, co stwarza możliwość popełnienia błędów podczas kopiowania/wklejania. Możesz zrobić enum
s public
i po prostu dodać dodatkową warstwę do ich dostępu.
Projekty, które mają tendencję do korzystania z tych funkcji, zwykle cierpią z powodu kruchej implementacji equals
, ponieważ zwykle są połączone z inną unikatową wartością, na przykład nazwą, która może zostać nieświadomie zduplikowana w całym kodzie w podobny, ale inny sposób. Używając całych kart, równość to freebie, która jest odporna na takie kruche zachowanie.
Główną wadą takiego systemu, poza szczegółowością, jest zamiana między kluczami unikatowymi na całym świecie (np. Przesyłanie do JSON). Jeśli są tylko kluczami, to można je bezpiecznie przywrócić (zduplikować) kosztem marnowania pamięci, ale używając słabości - equals
- to zaleta.
Istnieje obejście tego, który zapewnia globalną niepowtarzalność realizacji przez zaśmiecania go z anonimowego typu na światowym przykład:
public abstract class BasicMetaDataKey<T extends Serializable>
implements MetaDataKey<T>
{
private final T value;
public BasicMetaDataKey(T value)
{
this.value = value;
}
@Override
public T getValue()
{
return value;
}
// @Override equals
// @Override hashCode
}
public final class TypeSafeKeys
{
public static final MetaDataKey<String> A1 =
new BasicMetaDataKey<String>("value") {};
public static final MetaDataKey<Integer> A2 =
new BasicMetaDataKey<Integer>(0) {};
}
pamiętać, że każda instancja korzysta anonimową realizację, ale potrzebna jest nic innego, aby je realizować , więc {}
są puste. Jest to zarówno mylące, jak i denerwujące, ale działa, jeśli odwołania do instancji są lepsze, a bałagan jest ograniczony do minimum, chociaż może być nieco zagadkowy dla mniej doświadczonych programistów Java, przez co trudniej jest je utrzymać.
Wreszcie, jedynym sposobem na zapewnienie globalnej wyjątkowości i przeniesienie jest nieco bardziej twórcze z tym, co się dzieje.Najczęstszym zastosowaniem dla globalnie udostępnianych interfejsów, które widziałem są dla metadanych wiader, które mają tendencję do mieszania wielu różnych wartościach, z różnych typów (w T
, w przeliczeniu na podstawie klucza):
public interface MetaDataKey<T extends Serializable> extends Serializable
{
Class<T> getType();
String getName();
}
public final class TypeSafeKeys
{
public static enum StringKeys implements MetaDataKey<String>
{
A1;
@Override
public Class<String> getType() { return String.class; }
@Override
public String getName()
{
return getDeclaringClass().getName() + "." + name();
}
}
public static enum IntegerKeys implements MetaDataKey<Integer>
{
A2;
@Override
public Class<Integer> getType() { return Integer.class; }
@Override
public String getName()
{
return getDeclaringClass().getName() + "." + name();
}
}
public static final MetaDataKey<String> A1 = StringKeys.A1;
public static final MetaDataKey<Integer> A2 = IntegerKeys.A2;
}
Zapewnia to taka sama elastyczność, jak w przypadku pierwszej opcji, i zapewnia mechanizm do uzyskania odniesienia za pośrednictwem refleksji, jeśli okaże się to konieczne później, w związku z tym unikając potrzeby późniejszego określenia. Pozwala to również uniknąć wielu błędów związanych z kopiowaniem/wklejaniem, które zapewnia pierwsza opcja, ponieważ nie zostaną skompilowane, jeśli pierwsza metoda jest błędna, a druga metoda nie musi być zmieniana. Jedyna uwaga to to, że powinieneś upewnić się, że enum
, które mają być używane w ten sposób, to: public
, aby uniknąć błędów dostępu, ponieważ nie mają one dostępu do wewnętrznej domeny. jeśli nie chcesz, aby te przewody przechodziły przez przewód połączony z marshallem, możesz je ukryć przed zewnętrznymi paczkami, aby automatycznie je odrzucić (podczas marshallingu sprawdź odblaskowo, czy dostępny jest enum
, a jeśli nie, następnie zignoruj klucz/wartość). Nie ma nic zyskanego ani zagubionego przez uczynienie go public
, z wyjątkiem zapewnienia dwóch sposobów dostępu do instancji, jeśli bardziej oczywiste są odniesienia static
(ponieważ instancje enum
są po prostu takowe).
Po prostu żałuję, że nie zrobili tego, aby enum
s mógł rozszerzać obiekty w Javie. Może w Javie 9?
Ostateczna opcja tak naprawdę nie rozwiązuje twoich potrzeb, ponieważ prosiłeś o wartości, ale podejrzewam, że zbliża się to do rzeczywistego celu.
To podejście nie rozwiązuje mojego problemu, chyba że używam 'enum B implemts A
Właśnie o to chodzi - twój problem nie może zostać rozwiązany za pomocą pojedynczego 'Enum'. – Jorn
Teraz przynajmniej rozumiem, że słowo "enum" nie jest odpowiednie dla moich wymagań. Dzięki! –