[java] Java Enum 정의

Java generics를 꽤 잘 이해했다고 생각했지만 java.lang.Enum에서 다음을 발견했습니다.

class Enum<E extends Enum<E>>

누군가이 유형 매개 변수를 해석하는 방법을 설명 할 수 있습니까? 유사한 유형 매개 변수가 사용될 수있는 다른 예를 제공하기위한 보너스 포인트.



답변

열거 형의 형식 인수는 동일한 형식 인수를 가진 열거 형에서 파생되어야 함을 의미합니다. 어떻게 이런 일이 일어날 수 있습니까? 형식 인수를 새 형식 자체로 만듭니다. 따라서 StatusCode라는 열거 형이 있으면 다음과 같습니다.

public class StatusCode extends Enum<StatusCode>

이제 제약 조건을 확인하면 Enum<StatusCode>그렇게 E=StatusCode됩니다. 확인하자 : E확장 Enum<StatusCode>합니까? 예! 우린 괜찮아

이것의 요점이 무엇인지 스스로에게 물어 보는 것이 좋을 것이다. 글쎄, 그것은 Enum 용 API가 예를 들어 Enum<E>구현 하는 것을 말할 수있는 자체를 참조 할 수 있다는 것을 의미한다 Comparable<E>. 기본 클래스는 비교 (enum의 경우)를 수행 할 수 있지만 올바른 종류의 열거 만 서로 비교할 수 있습니다. (편집 : 글쎄, 거의-하단의 편집을 참조하십시오.)

ProtocolBuffers의 C # 포트에서 비슷한 것을 사용했습니다. “메시지”(불변) 및 “빌더”(변경 가능, 메시지 작성에 사용됨)가 있으며 유형 쌍으로 제공됩니다. 관련된 인터페이스는 다음과 같습니다.

public interface IBuilder<TMessage, TBuilder>
  where TMessage : IMessage<TMessage, TBuilder>
  where TBuilder : IBuilder<TMessage, TBuilder>

public interface IMessage<TMessage, TBuilder>
  where TMessage : IMessage<TMessage, TBuilder>
  where TBuilder : IBuilder<TMessage, TBuilder>

즉, 메시지에서 적절한 빌더를 얻을 수 있으며 (예 : 메시지 사본을 가져와 비트를 변경) 빌더에서 메시지 작성을 마치면 적절한 메시지를 얻을 수 있습니다. API를 사용하는 사용자는 실제로 이것을 신경 쓸 필요가 없습니다. 끔찍하게 복잡하고 어디에 있는지 알기 위해 여러 번 반복했습니다.

편집 : 이것으로도 자체가 괜찮지 만 같은 유형이 아닌 유형 인수를 사용하는 홀수 유형을 만드는 것을 막을 수는 없습니다. 목적은 잘못된 경우 로부터 보호하기보다는 올바른 경우에 혜택을 제공 하는 입니다.

따라서 EnumJava에서 “특별히”처리되지 않은 경우 (주석에서 언급 한대로) 다음 유형을 작성할 수 있습니다.

public class First extends Enum<First> {}
public class Second extends Enum<First> {}

SecondComparable<First>오히려 구현할 Comparable<Second>것이지만 First그 자체로는 괜찮을 것입니다.


답변

다음은이 책에서 설명의 수정 된 버전 인 자바 제네릭과 컬렉션 : 우리는 가지고 Enum선언

enum Season { WINTER, SPRING, SUMMER, FALL }

수업으로 확장됩니다

final class Season extends ...

어디서나 ...열거 형의 매개 변수가있는 기본 클래스가되어야합니다. 무엇이 필요한지 알아 봅시다. 요구 사항 중 하나 Season는 구현해야한다는 것 Comparable<Season>입니다. 그래서 우리는 필요합니다

Season extends ... implements Comparable<Season>

이 기능을 사용하기 위해 무엇을 사용할 수 ...있습니까? 이 매개 변수화 여야하므로 Enum다음 중 하나를 선택할 Enum<Season>수 있습니다.

Season extends Enum<Season>
Enum<Season> implements Comparable<Season>

따라서 Enum같은 유형에 매개 변수화됩니다 Season. 의 초록 Season과 매개 변수는 Enum만족하는 모든 유형입니다.

 E extends Enum<E>

Maurice Naftalin (공동 저자, Java Generics and Collections)


답변

이것은 간단한 예제와 서브 클래스에 대한 체인 메소드 호출을 구현하는 데 사용할 수있는 기술로 설명 할 수 있습니다. 아래 예제에서는 체인이 작동하지 않도록 setName반환 Node합니다 City.

class Node {
    String name;

    Node setName(String name) {
        this.name = name;
        return this;
    }
}

class City extends Node {
    int square;

    City setSquare(int square) {
        this.square = square;
        return this;
    }
}

public static void main(String[] args) {
    City city = new City()
        .setName("LA")
        .setSquare(100);    // won't compile, setName() returns Node
}

따라서 일반 선언에서 하위 클래스를 참조 할 수 있으므로 City이제 올바른 유형을 반환합니다.

abstract class Node<SELF extends Node<SELF>>{
    String name;

    SELF setName(String name) {
        this.name = name;
        return self();
    }

    protected abstract SELF self();
}

class City extends Node<City> {
    int square;

    City setSquare(int square) {
        this.square = square;
        return self();
    }

    @Override
    protected City self() {
        return this;
    }

    public static void main(String[] args) {
       City city = new City()
            .setName("LA")
            .setSquare(100);                 // ok!
    }
}


답변

그게 무슨 뜻인지 궁금한 사람은 아닙니다. 혼란스러운 Java 블로그를 참조하십시오 .

“클래스가이 클래스를 확장하면 매개 변수 E를 전달해야합니다. 매개 변수 E의 경계는이 매개 변수를 동일한 매개 변수 E로 확장하는 클래스에 대한 것입니다.”


답변

이 게시물은 ‘재귀 적 제네릭 형식’이라는 문제를 완전히 명확하게 설명했습니다. 이 특정 구조가 필요한 다른 사례를 추가하고 싶었습니다.

일반 그래프에 일반 노드가 있다고 가정하십시오.

public abstract class Node<T extends Node<T>>
{
    public void addNeighbor(T);

    public void addNeighbors(Collection<? extends T> nodes);

    public Collection<T> getNeighbor();
}

그런 다음 특수한 유형의 그래프를 가질 수 있습니다.

public class City extends Node<City>
{
    public void addNeighbor(City){...}

    public void addNeighbors(Collection<? extends City> nodes){...}

    public Collection<City> getNeighbor(){...}
}


답변

Enum소스 코드 를 보면 다음과 같습니다.

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {

    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }

    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
    }

    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    }
}

먼저 무엇을 E extends Enum<E>의미합니까? 이것은 type 매개 변수가 Enum에서 확장되어 원시 형식으로 매개 변수화되지 않는다는 것을 의미합니다 (자체적으로 매개 변수화 됨).

열거 형이있는 경우 관련이 있습니다.

public enum MyEnum {
    THING1,
    THING2;
}

내가 올바르게 알면

public final class MyEnum extends Enum<MyEnum> {
    public static final MyEnum THING1 = new MyEnum();
    public static final MyEnum THING2 = new MyEnum();
}

따라서 MyEnum은 다음과 같은 메소드를받습니다.

public final int compareTo(MyEnum o) {
    Enum<?> other = (Enum<?>)o;
    Enum<MyEnum> self = this;
    if (self.getClass() != other.getClass() && // optimization
        self.getDeclaringClass() != other.getDeclaringClass())
        throw new ClassCastException();
    return self.ordinal - other.ordinal;
}

그리고 더 중요한 것은

    @SuppressWarnings("unchecked")
    public final Class<MyEnum> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<MyEnum>)clazz : (Class<MyEnum>)zuper;
    }

이 만드는 getDeclaringClass()적절한에 캐스팅 Class<T>개체를.

더 명확한 예는 제네릭 바운드를 지정하려는 경우이 구성을 피할 수없는 이 질문에 대답 한 것입니다 .


답변

Wikipedia에 따르면이 패턴을 Curiously recurring template pattern 이라고 합니다. 기본적으로 CRTP 패턴을 사용하면 유형 캐스팅없이 하위 클래스 유형을 쉽게 참조 할 수 있습니다. 즉, 패턴을 사용하여 가상 기능을 모방 할 수 있습니다.