development

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

big-blog 2020. 6. 26. 07:46
반응형

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>,
    ...
}

각 열거 형 리터럴은 generic 형식을 기반으로 추가 속성 <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;
    }

    ...

}

Because you can't. Seriously. That could be added to the language spec. It hasn't been. It would add some complexity. That benefit to cost means it isn't a high priority.

Update: Currently being added to the language under JEP 301: Enhanced Enums.


There are other methods in ENUM that wouldn't work. What would MyEnum.values() return?

What about MyEnum.valueOf(String name)?

For the valueOf if you think that compiler could make generic method like

public static MyEnum valueOf(String name);

in order to call it like MyEnum<String> myStringEnum = MyEnum.value("some string property"), that wouldn't work either. For example what if you call MyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property") ? It is not possible to implement that method to work correctly, for example to throw exception or return null when you call it like MyEnum.<Int>value("some double property") because of type erasure.


Frankly this seems like more of a solution in search of a problem than anything.

The entire purpose of the java enum is to model a enumeration of type instances that share similiar properties in a way that provides consistency and richness beyond that of comparable String or Integer representations.

Take an example of a text book enum. This is not very useful or consistent:

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

Why would I want my different planets to have different generic type conversions? What problem does it solve? Does it justify complicating the language semantics? If I do need this behavior is an enum the best tool to achieve it?

Additionally how would you manage complex conversions?

for Instance

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

Its easy enough with String Integer to supply the name or ordinal. But generics would allow you to supply any type. How would you manage the conversion to MyComplexClass? Now your mucking up two constructs by forcing the compiler to know that there are a limited subset of types that can be supplied to generic enums and introducing additional confusion to concept(Generics) that already seems elude a lot of programmers.


Becasue "enum" is the abbreviation for enumeration. It's just a set of named constants that stand in place of ordinal numbers to make the code better readable.

I don't see what the intended meaning of a type-parameterized constant could be.


I think because basically Enums can't be instanced

Where would you set the T class, if JVM allowed you to do so?

Enumeration is data that is supposed to be always the same, or at least, that it won't change dinamically.

new MyEnum<>()?

Still the following approach may be useful

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;
    }   
}

참고URL : https://stackoverflow.com/questions/4290878/why-shouldnt-java-enum-literals-be-able-to-have-generic-type-parameters

반응형