[java] 주석 필드의 기본 null 값을 설정하는 동안 오류가 발생했습니다.

“속성 값은 상수 여야합니다”라는 오류가 발생하는 이유는 무엇입니까? null 상수 가 아닙니까 ???

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class<? extends Foo> bar() default null;// this doesn't compile
}



답변

이유는 모르겠지만 JLS 는 매우 명확합니다.

 Discussion

 Note that null is not a legal element value for any element type.

기본 요소의 정의는 다음과 같습니다.

     DefaultValue:
         default ElementValue

불행히도 새로운 언어 기능 (Enums 및 현재 Annotations)에 언어 사양을 충족하지 않을 때 매우 도움이되지 않는 컴파일러 오류 메시지가 있음을 계속 발견합니다.

편집 : 약간의 googleing 이 JSR-308에서 다음 을 발견 했습니다.이 상황에서 null을 허용한다고 주장합니다.

제안에 대해 몇 가지 이의가있을 수 있습니다.

이 제안은 이전에는 불가능했던 어떤 것도 가능하게하지 않습니다.

프로그래머가 정의한 특수 값은 “없음”, “초기화되지 않음”, null 자체 등을 의미 할 수있는 null보다 더 나은 문서를 제공합니다.

이 제안은 오류가 발생하기 쉽습니다. 명시적인 값을 확인하는 것을 잊어 버리는 것보다 null에 대한 확인을 잊어 버리는 것이 훨씬 쉽습니다.

제안은 표준 관용구를 더 장황하게 만들 수 있습니다. 현재 주석 사용자 만 특수 값을 확인하면됩니다. 제안을 통해 주석을 처리하는 많은 도구는 널 포인터 예외가 발생하지 않도록 필드 값이 널인지 여부를 확인해야합니다.

나는 마지막 두 점만이 “처음에 그것을하지 않는 이유”와 관련이 있다고 생각한다. 마지막 요점은 확실히 좋은 점을 제시합니다. 주석 프로세서는 주석 값에 대해 null을 얻을 것이라고 걱정할 필요가 없습니다. 나는 주석 프로세서와 다른 프레임 워크 코드가 개발자의 코드를 다른 방식보다 명확하게 만들기 위해 그런 종류의 검사를해야하는 일이 더 많다는 것을 보는 경향이 있지만, 그것을 변경하는 것을 정당화하기는 확실히 어렵습니다.


답변

이 시도

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class bar() default void.class;
}

새 클래스가 필요하지 않으며 이미 아무 의미가없는 Java의 키워드입니다.


답변

JLS가 이것에 대해 매우 모호하지만 이것은 불법으로 보일 것입니다.

주석에 대한 Class 속성이있는 기존 주석을 생각하기 위해 기억을 곤두 세우고 JAXB API에서이 주석을 기억했습니다.

@Retention(RUNTIME) @Target({PACKAGE,FIELD,METHOD,TYPE,PARAMETER})
public @interface XmlJavaTypeAdapter {
    Class type() default DEFAULT.class;

    static final class DEFAULT {}
}

null과 동등한 것을 보유하기 위해 더미 정적 클래스를 어떻게 정의해야했는지 알 수 있습니다.

불쾌한.


답변

한 가지 다른 방법이있는 것 같습니다.

나는 이것도 좋아하지 않지만 작동 할 수 있습니다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeInterface {
    Class<? extends Foo>[] bar() default {};
}

따라서 기본적으로 대신 빈 배열을 만듭니다. 이것은 기본값을 허용하는 것 같습니다.


답변

Class<? extends Foo> bar() default null;// this doesn't compile

언급했듯이 Java 언어 사양은 주석 기본값에서 null 값을 허용하지 않습니다.

내가하는 경향 DEFAULT_VALUE은 주석 정의 상단에 상수를 정의하는 것입니다. 다음과 같은 것 :

public @interface MyAnnotation {
    public static final String DEFAULT_PREFIX = "__class-name-here__ default";
    ...
    public String prefix() default DEFAULT_PREFIX;
}

그런 다음 내 코드에서 다음과 같은 작업을 수행합니다.

if (myAnnotation.prefix().equals(MyAnnotation.DEFAULT_PREFIX)) { ... }

클래스의 경우 마커 클래스를 정의합니다. 불행히도 이에 대한 상수를 가질 수 없으므로 대신 다음과 같은 작업을 수행해야합니다.

public @interface MyAnnotation {
    public static Class<? extends Foo> DEFAULT_BAR = DefaultBar.class;
    ...
    Class<? extends Foo> bar() default DEFAULT_BAR;
}

귀하의 DefaultFoo클래스는 빈 구현은 그렇게 코드가 할 수있는 것 :

if (myAnnotation.bar() == MyAnnotation.DEFAULT_BAR) { ... }

도움이 되었기를 바랍니다.


답변

이 오류 메시지는 오해의 소지가 있지만, null리터럴은 여기 에있는 Java 언어 사양에 정의 된 상수 표현식이 아닙니다 .

또한 Java 언어 사양에 따르면

요소 유형이 지정된 기본값과 일치 하지 않는 경우 (§9.7) 컴파일 타임 오류 입니다.

그리고 그것이 의미하는 바를 설명 하기 위해

요소 유형이 요소 값과 일치하지 않으면 컴파일 타임 오류입니다. 요소 유형 T는 다음 중 하나가 참인 경우에만 요소 값 V에 상응 합니다.

  • T배열 유형 E[]이고 다음 중 하나입니다.
    • […]
  • T어레이 타입이다 , 그리고 타입 V할당 호환 (§5.2)가 함께 T, 그리고 :
    • 경우 T기본 유형 또는 String그 후, V일정한 표현 (§15.28)이다.
    • 경우 T입니다 Class또는 호출 Class(§4.5), 다음 V클래스 리터럴 (§15.8.2)입니다.
    • 경우 T열거 형 (§8.9) 인 다음 V열거 상수 (§8.9.1)이다.
    • V가 null이 아닙니다 .

따라서 여기에서 요소 유형 Class<? extends Foo>은 기본값과 일치하지 않습니다 null.


답변

다른 사람들이 말했듯이 null은 Java에서 유효한 기본값이 아니라는 규칙이 있습니다.

필드가 가질 수있는 가능한 값의 수가 제한된 경우이 문제를 해결하기위한 한 가지 옵션은 필드 유형을 자체적으로 null에 대한 매핑을 포함하는 사용자 정의 열거 형으로 만드는 것입니다. 예를 들어 기본 null을 원하는 다음 주석이 있다고 가정합니다.

public @interface MyAnnotation {
   Class<?> value() default null;
}

우리가 확립했듯이 이것은 허용되지 않습니다. 대신 우리가 할 수있는 것은 열거 형을 정의하는 것입니다 :

public enum MyClassEnum {
    NULL(null),
    MY_CLASS1(MyClass1.class),
    MY_CLASS2(MyClass2.class),
    ;

    public final Class<?> myClass;

    MyClassEnum(Class<?> aClass) {
        myClass = aClass;
    }
}

주석을 다음과 같이 변경하십시오.

public @interface MyAnnotation {
  MyClassEnum value() default MyClassEnum.NULL;
}

이것의 주된 단점은 (가능한 값을 열거 할 필요가 없다는 점을 제외하고) 이제 값을 열거 형으로 래핑했기 때문에 값을 얻으려면 열거 형에서 속성 / 게터를 호출해야합니다.