제네릭으로 열거 형을 구현하는 방법은 무엇입니까?
다음과 같은 일반 인터페이스가 있습니다.
interface A<T> {
T getValue();
}
이 인터페이스에는 제한된 인스턴스가 있으므로 열거 형 값으로 구현하는 것이 가장 좋습니다. 문제는 해당 인스턴스에 다른 유형의 값이 있으므로 다음 접근 방식을 시도했지만 컴파일되지 않는다는 것입니다.
public enum B implements A {
A1<String> {
@Override
public String getValue() {
return "value";
}
},
A2<Integer> {
@Override
public Integer getValue() {
return 0;
}
};
}
이것에 대해 어떤 생각?
당신은 할 수 없습니다. Java는 열거 형 상수에 대한 제네릭 유형을 허용하지 않습니다. 그러나 열거 형 유형에서는 허용됩니다.
public enum B implements A<String> {
A1, A2;
}
이 경우에 할 수있는 것은 각 제네릭 유형에 대한 열거 형 유형을 갖거나 클래스로 만들어서 열거 형을 갖는 '가짜'입니다.
public class B<T> implements A<T> {
public static final B<String> A1 = new B<String>();
public static final B<Integer> A2 = new B<Integer>();
private B() {};
}
불행히도 둘 다 단점이 있습니다.
특정 API를 설계하는 Java 개발자로서 우리는이 문제를 자주 접하게됩니다. 이 게시물을 보았을 때 내 자신의 의심을 재확인하고 있었지만 이에 대한 자세한 해결 방법이 있습니다.
// 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;
}
그 시점에서, 당신은 진정한 상수 인의 이익을 얻을 enum
아니라의 고유 구현 것으로, eration 값을 (그리고 그와 함께 갈 특권을 모두) interface
,하지만 당신은 원하는 글로벌 접근성을 가지고 enum
.
분명히 이것은 자세한 내용을 추가하여 복사 / 붙여 넣기 실수의 가능성을 만듭니다. 당신은 만들 수 enum
들 public
과 단순히 액세스에 추가 레이어를 추가합니다.
이러한 기능을 사용하는 경향이있는 디자인 equals
은 일반적으로 이름과 같은 다른 고유 한 값과 결합되어 유사하지만 다른 목적을 위해 코드베이스에서 무의식적으로 복제 될 수 있기 때문에 취약한 구현 으로 어려움을 겪는 경향 이 있습니다. enum
전반적으로 s 를 사용함으로써 평등은 그러한 취성 행동에 영향을받지 않는 공짜입니다.
이러한 시스템의 주요 단점은 자세한 정보를 넘어서 전역 적으로 고유 한 키 (예 : JSON과의 마샬링)간에 앞뒤로 변환한다는 아이디어입니다. 키일 경우 메모리를 낭비하는 대신 안전하게 다시 인스턴스화 (복제) 할 수 있지만 이전에는 약점을 equals
장점으로 사용합니다.
전역 인스턴스 당 익명 유형으로 복잡하게하여 전역 구현 고유성을 제공하는 해결 방법이 있습니다.
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) {};
}
Note that each instance uses an anonymous implementation, but nothing else is needed to implement it, so the {}
are empty. This is both confusing and annoying, but it works if instance references are preferable and clutter is kept to a minimum, although it may be a bit cryptic to less experienced Java developers, thereby making it harder to maintain.
Finally, the only way to provide global uniqueness and reassignment is to be a little more creative with what is happening. The most common use for globally shared interfaces that I have seen are for MetaData buckets that tend to mix a lot of different values, with different types (the T
, on a per key basis):
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;
}
This provides the same flexibility as the first option, and it provides a mechanism for obtaining a reference via reflection, if it becomes necessary later, therefore avoiding the need for instantiable later. It also avoids a lot of the error prone copy/paste mistakes that the first option provides because it won't compile if the first method is wrong, and the second method does not need to change. The only note is that you should ensure that the enum
s meant to be used in that fashion are public
to avoid anyone getting access errors because they do not have access to the inner enum
; if you did not want to have those MetaDataKey
s going across a marshaled wire, then keeping them hidden from outside packages could be used to automatically discard them (during marshaling, reflectively check to see if the enum
is accessible, and if it is not, then ignore the key/value). There is nothing gained or lost by making it public
except providing two ways to access the instance, if the more obvious static
references are maintained (as the enum
instances are just that anyway).
I just wish that they made it so that enum
s could extend objects in Java. Maybe in Java 9?
The final option does not really solve your need, as you were asking for values, but I suspect that this gets toward the actual goal.
If JEP 301: Enhanced Enums gets accepted, then you will be able to use syntax like this (taken from proposal):
enum Primitive<X> {
INT<Integer>(Integer.class, 0) {
int mod(int x, int y) { return x % y; }
int add(int x, int y) { return x + y; }
},
FLOAT<Float>(Float.class, 0f) {
long add(long x, long y) { return x + y; }
}, ... ;
final Class<X> boxClass;
final X defaultValue;
Primitive(Class<X> boxClass, X defaultValue) {
this.boxClass = boxClass;
this.defaultValue = defaultValue;
}
}
ReferenceURL : https://stackoverflow.com/questions/11490485/how-to-implement-enum-with-generics
'UFO ET IT' 카테고리의 다른 글
Java 직렬화-java.io.InvalidClassException 로컬 클래스가 호환되지 않습니다. (0) | 2020.12.30 |
---|---|
JSLint가 " 'return'후 예기치 않은 'else'"에 대해 불평하는 이유는 무엇입니까? (0) | 2020.12.30 |
새로운 JSLint 오류 "탭이 아닌 공백 사용"및 "안전하지 않은 문자"가 도입 된 이유는 무엇입니까? (0) | 2020.12.30 |
유니 코드로 강제 변환 : 문자열 또는 버퍼 필요, django 관리자에서 렌더링 할 때 NoneType 발견 (0) | 2020.12.30 |
C ++ 14에서 람다를 통해 std :: bind를 사용하는 이유는 무엇입니까? (0) | 2020.12.30 |