[java] Java 열거 리터럴이 일반 유형 매개 변수를 가질 수없는 이유는 무엇입니까?

Java 열거 형은 훌륭합니다. 제네릭도 마찬가지입니다. 물론 우리는 유형 삭제로 인해 후자의 한계를 알고 있습니다. 그러나 이해할 수없는 한 가지가 있습니다. 왜 이런 식으로 열거 형을 만들 수 없습니까?

public enum MyEnum<T> {
    LITERAL1<String>,
    LITERAL2<Integer>,
    LITERAL3<Object>;
}

이 제네릭 형식 매개 변수 <T>는 여러 곳에서 유용 할 수 있습니다. 메소드에 일반 유형 매개 변수를 상상해보십시오.

public <T> T getValue(MyEnum<T> param);

또는 열거 형 클래스 자체에서도 :

public T convert(Object o);

보다 구체적인 예 # 1

위의 예는 일부에서는 너무 추상적으로 보일 수 있으므로 여기에 내가 원하는 이유에 대한 더 실제적인 예가 있습니다. 이 예에서는 사용하고 싶습니다

  • 열거 형, 유한 속성 키 집합을 열거 할 수 있기 때문에
  • 제네릭 (Generics) : 속성 저장을위한 메서드 수준 형식 안전성을 가질 수 있기 때문에
public interface MyProperties {
     public <T> void put(MyEnum<T> key, T value);
     public <T> T get(MyEnum<T> key);
}

보다 구체적인 예 # 2

데이터 유형 열거가 있습니다.

public interface DataType<T> {}

public enum SQLDataType<T> implements DataType<T> {
    TINYINT<Byte>,
    SMALLINT<Short>,
    INT<Integer>,
    BIGINT<Long>,
    CLOB<String>,
    VARCHAR<String>,
    ...
}

각 열거 형 리터럴에는 분명히 제네릭 형식을 기반으로 추가 속성이 있습니다. <T> 을 갖는 동시에 열거 형 (불변, 단일 톤, 열거 가능 등)입니다.

질문:

아무도 이것을 생각하지 않았습니까? 이것이 컴파일러 관련 제한입니까? 사실 키워드 ” enum “이 JVM에 생성 된 코드를 나타내는 구문 설탕으로 구현 된다는 점을 고려하면 이 제한을 이해하지 못합니다.

누가 나에게 설명 할 수 있습니까? 대답하기 전에 다음을 고려하십시오.

  • 일반 유형이 지워지는 것을 알고 있습니다 🙂
  • Class 객체를 사용하는 해결 방법이 있다는 것을 알고 있습니다. 그들은 해결 방법입니다.
  • 제네릭 형식은 해당되는 경우 (예 : convert () 메서드 호출시) 컴파일러 생성 형식 캐스트를 생성합니다.
  • 제네릭 형식 <T>는 열거 형에 있습니다. 따라서 열거 형의 각 리터럴에 바인딩됩니다. 따라서 컴파일러는 다음과 같은 내용을 작성할 때 적용 할 유형을 알고 있습니다.String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
  • T getvalue()메소드 의 일반 유형 매개 변수에도 동일하게 적용됩니다 . 컴파일러는 호출 할 때 타입 캐스팅을 적용 할 수 있습니다String string = someClass.getValue(LITERAL1)


답변

이것은 현재 JEP-301 Enhanced Enums 에서 논의되고 있습니다. JEP에 주어진 예는 바로 내가 찾던 것입니다.

enum Argument<X> { // declares generic enum
   STRING<String>(String.class),
   INTEGER<Integer>(Integer.class), ... ;

   Class<X> clazz;

   Argument(Class<X> clazz) { this.clazz = clazz; }

   Class<X> getClazz() { return clazz; }
}

Class<String> cs = Argument.STRING.getClazz(); //uses sharper typing of enum constant

불행히도 JEP는 여전히 다음과 같은 중요한 문제로 어려움을 겪고 있습니다. http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html


답변

대답은 질문에 있습니다.

유형 삭제 때문에

인수 유형이 지워 지므로이 두 가지 방법 중 어느 것도 사용할 수 없습니다.

public <T> T getValue(MyEnum<T> param);
public T convert(Object);

이러한 방법을 실현하려면 다음과 같이 열거 형을 구성 할 수 있습니다.

public enum MyEnum {
    LITERAL1(String.class),
    LITERAL2(Integer.class),
    LITERAL3(Object.class);

    private Class<?> clazz;

    private MyEnum(Class<?> clazz) {
      this.clazz = clazz;
    }

    ...

}


답변

당신이 할 수 없기 때문에. 진심으로. 언어 사양에 추가 할 수 있습니다. 그렇지 않았습니다. 약간의 복잡성이 추가됩니다. 비용에 대한 이점은 우선 순위가 높지 않다는 것을 의미합니다.

업데이트 : 현재 JEP 301 : Enhanced Enums 의 언어에 추가되고 있습니다 .


답변

ENUM에는 작동하지 않는 다른 방법이 있습니다. 어떤 것MyEnum.values() 돌아 올까요?

이건 어떤가요 MyEnum.valueOf(String name) ?

컴파일러가 다음과 같은 일반적인 방법을 만들 수 있다고 생각하면 valueOf

공개 정적 MyEnum valueOf (문자열 이름);

처럼 호출하면 MyEnum<String> myStringEnum = MyEnum.value("some string property")작동하지 않습니다. 예를 들어 전화하면 MyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property")어떻게됩니까? 예를 들어 MyEnum.<Int>value("some double property")삭제 유형으로 인해 호출 할 때 예외를 발생 시키거나 null을 반환하는 것과 같이 해당 메소드를 올바르게 작동하도록 구현할 수 없습니다 .


답변

솔직히 이것은 무엇보다 문제를 찾아내는 더 많은 해결책처럼 보입니다.

Java 열거 형의 전체 목적은 비슷한 String 또는 Integer 표현보다 일관성과 풍부 성을 제공하는 방식으로 비슷한 특성을 공유하는 유형 인스턴스의 열거를 모델링하는 것입니다.

교과서 열거 형의 예를 들어보십시오. 이것은 매우 유용하거나 일관성이 없습니다.

public enum Planet<T>{
    Earth<Planet>,
    Venus<String>,
    Mars<Long>
    ...etc.
}

다른 행성에서 다른 일반 유형 변환을 원하는 이유는 무엇입니까? 어떤 문제가 해결됩니까? 언어 의미를 복잡하게하는 것이 정당합니까? 이 동작이 필요한 경우이를 달성하기위한 최고의 도구입니다.

또한 복잡한 전환을 어떻게 관리 하시겠습니까?

인스턴스

public enum BadIdea<T>{
   INSTANCE1<Long>,
   INSTANCE2<MyComplexClass>;
}

String Integer이름이나 서수를 제공 하기에 충분 합니다. 그러나 제네릭을 사용하면 모든 유형을 제공 할 수 있습니다. 로의 전환을 MyComplexClass어떻게 관리 하시겠습니까? 이제 컴파일러가 제네릭 열거 형에 제공 할 수있는 제한된 유형의 하위 집합이 있음을 알고 이미 많은 프로그래머를 피할 수없는 개념 (Generics)에 대한 추가 혼란을 도입함으로써 두 가지 구문을 모색했습니다.


답변

“enum”은 열거 형의 약자입니다. 코드를 읽기 쉽도록 서수 대신에 명명 된 상수 세트 일뿐입니다.

형식 매개 변수 상수의 의도 된 의미가 무엇인지 알 수 없습니다.


답변

기본적으로 열거 형을 인스턴스화 할 수 없기 때문에

JVM에서 허용 한 경우 T 클래스를 어디에 설정 하시겠습니까?

열거는 항상 동일하거나 최소한 데이터가 크게 바뀌지 않아야하는 데이터입니다.

새로운 MyEnum <> ()?

여전히 다음 접근법이 유용 할 수 있습니다.

public enum MyEnum{

    LITERAL1("s"),
    LITERAL2("a"),
    LITERAL3(2);

    private Object o;

    private MyEnum(Object o) {
        this.o = o;
    }

    public Object getO() {
        return o;
    }

    public void setO(Object o) {
        this.o = o;
    }
}