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


79

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

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

누군가 (컴파일 또는 해결 방법) 이것이 필요한 이유는 무엇입니까? 에서 이미 동일한 "기능"을 부여했습니다 Class<? extends Foo> bar();.
xerx593

3
디폴트 더 이상 없기 때문에 때문에, 필드는 주석 사용량이 필수가된다
도나 텔로

답변:


64

이유는 모르겠지만 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을 얻을 것이라고 걱정할 필요가 없습니다. 나는 주석 프로세서와 다른 프레임 워크 코드가 개발자의 코드를 다른 방식보다 명확하게 만들기 위해 그런 종류의 검사를해야하는 일이 더 많다는 것을 보는 경향이 있지만, 그것을 변경하는 것을 정당화하기는 확실히 어렵습니다.


67

이 시도

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

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


8
나는 그것을 좋아한다. 유일한 문제는 – 원래 질문의 맥락에서 –이 답변이 타입 매개 변수를 지원 Class<? extends Foo> bar() default void.null 하지 않는다는 것입니다 : 컴파일되지 않습니다.
Kariem 2013

3
void.class는 일반적으로 적용 할 수 없습니다. public @interface NoNullDefault {String value () default void.class; }
wjohnson 2014 dec.

Groovy의 경우 댓글에 언급 된 경우에도 잘 작동합니다
Vampire

LOL : 그것은 "아무것도"를 의미하는 것에 비해 아무것도 의미하지 않습니다
Andreas

55

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과 동등한 것을 보유하기 위해 더미 정적 클래스를 어떻게 정의해야했는지 알 수 있습니다.

불쾌한.


3
예, 비슷한 작업을 수행합니다 (Foo.class를 기본값으로 사용). 짜증나.
ripper234

7
그리고 String 필드의 경우 매직 스트링 에 의존해야 합니다 . 분명히 잘 알려진 반 패턴은 이제 잘 알려진 언어 기능보다 낫습니다.
Tim Yates

3
@TimYates null을 사용할 수 있고 보편적으로 이해 될 때 자신의 "가치없는"센티넬 값을 정의하는 사람들을 보는 것은 나를 죽입니다. 그리고 이제 그것은 언어 자체에 의해 요구되고 있습니까?! 운 좋게도 "매직 문자열"을 공개 상수로 정의 할 수 있습니다. 여전히 이상적이지는 않지만 적어도 주석을 찾는 코드는 모든 곳에서 리터럴을 사용하는 대신 상수를 참조 할 수 있습니다.
spaaarky21 2014 년

11

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

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

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

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


3
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) { ... }

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


덧붙여서, 나는 이것을 String으로 시도했지만 작동하지 않습니다. 동일한 String 객체를 반환하지 않습니다.
Doradus

@Doradus가 .equals()아니라 사용 했습니까 ==? 다른 이나 다른 물건 을 얻었습니까?
Gray

다른 개체. 나는 ==.
Doradus

클래스는 @Doradus 싱글 톤입니다. 나는 String이 싱글 톤이 될 것이라고 예상했지만 그렇지 않다고 생각하고 .equals(). 놀라운.
Gray

1

이 오류 메시지는 오해의 소지가 있지만, 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.


귀하의 솔루션은 복사 및 붙여 넣기에 적합하지 않습니다 ;-) 일부는 읽을 때 생각하는 것을 좋아하지 않습니다
Matthias M

1
IMHO 예, 당신은 그랬습니다 (그러나 그것은 내가 아닙니다). 귀하의 식은 "when (...) 또는 (V is not null)"과 같이 표시되며 이는 null이 아닌 값이 옳은 것처럼 들립니다. JLS 공식은 진정한 짐승이지만, 빼놓을 수없는 외부 or와 내부 and가 있습니다.
maaartinus

@maaartinus 오래 전에 귀하의 의견을 보지 못했고 전체 견적을 추가하기 위해 내 답변을 업데이트했습니다.
Sotirios Delimanolis 2019

1

다른 사람들이 말했듯이 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;
}

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.