Java에서 상수 클래스를 어떻게 정의합니까?


89

상수를 보유하는 클래스를 정의해야한다고 가정합니다.

public static final String SOME_CONST = "SOME_VALUE";

이 작업을 수행하는 데 선호되는 방법은 무엇입니까?

  1. 상호 작용
  2. 추상 클래스
  3. 최종 수업

어떤 것을 사용해야하며 그 이유는 무엇입니까?


일부 답변에 대한 설명 :

열거 형 - 열거 형 을 사용하지 않을 것입니다. 아무것도 열거하지 않고 어떤 식 으로든 서로 관련이없는 상수를 수집합니다.

인터페이스 - 인터페이스 를 구현하는 클래스로 설정하지 않을 것입니다. 인터페이스를 사용하여 다음과 같이 상수를 호출하고 싶습니다 ISomeInterface.SOME_CONST.


여기에 비슷한 논의가 있습니다 : stackoverflow.com/questions/320588/… . 인스턴스화 할 수 없도록 개인 생성자와 함께 최종 클래스를 사용합니다.
Dan Dyer

2
미안하지만 "나는 열거 형을 사용하지 않을 것입니다"는이 질문을 "어리석은 일을하는 가장 좋은 방법은 무엇입니까?"로 바꿉니다.
cletus

나는 당신이 인터페이스를 구현할 것이라고 말하는 것이 아닙니다. 그러나이를 위해 인터페이스를 사용할 필요가 없습니다. 그러니 마지막 수업으로
가세요 :)

Enum의 문제점은 무엇입니까? 항상 그것을 사용하여 '어떤 식 으로든 서로 관련이없는 일부 상수'를 수집 할 수 있습니다. 흠?
gedevan

5
개념적으로 열거 형은 상수가 관련되지 않은 경우 잘못된 선택입니다. 열거 형은 동일한 유형의 대체 값을 나타냅니다. 이 상수는 대안이 아니며 그들은 심지어 동일한 유형 (일부있을 수 있습니다 문자열, 일부 정수 등)하지 않을 수 있습니다
댄 다이어

답변:


92

최종 수업을 사용하십시오. 단순함을 위해 정적 가져 오기를 사용하여 다른 클래스에서 값을 재사용 할 수 있습니다.

public final class MyValues {
  public static final String VALUE1 = "foo";
  public static final String VALUE2 = "bar";
}

다른 수업에서 :

import static MyValues.*
//...

if(variable.equals(VALUE1)){
//...
}

4
여기에서 별도의 클래스를 생성하면 어떤 이점이 있습니까? 일반적으로 Calendar API를 디자인의 좋은 예로 들지는 않지만 "Calendar 관련 상수는 Calendar 클래스에 있습니다"라는 측면에서 괜찮습니다.
Jon Skeet

7
장점은 둘 이상의 클래스에서 상수를 재사용해야하는 경우 코드를 복제하지 않는 것입니다. 이것의 장점을 쉽게 알 수 있다고 생각합니다.
user54579

2
코드를 복제하는 이유는 무엇입니까? 다른 클래스를 참조하십시오. 상수가 실제로 홀로 서있는 것은 비교적 드뭅니다.
Jon Skeet

14
더 나은 가독성을 위해 본문 (및 일치하는 주석)이없는 개인 기본 생성자도 있습니다.
Ran Biron

5
꽤 오래된 대답 이며이 의견은 아마도 VALUE1.equals(variable)적절하지 않지만 NPE를 피하기 때문에 사용할 것 입니다.
Aakash

38

귀하의 설명은 다음과 같습니다. "열거 형을 사용하지 않을 것입니다. 아무 것도 열거하지 않고 어떤 식 으로든 서로 관련이없는 일부 상수 만 수집합니다."

상수가 서로 전혀 관련이없는 경우 왜 함께 수집하고 싶습니까? 가장 밀접하게 관련된 클래스에 각 상수를 넣으십시오.


2
Jon-모두 동일한 기능에 속한다는 점에서 관련이 있습니다. 그러나 그것들은 어떤 것의 열거가 아닙니다. 그들은 객체가 "그것들 중 하나"라는 속성을 가지고 있지 않습니다.
Yuval Adam

13
괜찮아. 이 경우 관련된 기능에 가장 가까운 클래스에 넣으십시오.
Jon Skeet

26

내 제안 (내림차순) :

1) 하지 마십시오 . 가장 관련성이 높은 실제 클래스에서 상수를 만듭니다. '상수 가방'클래스 / 인터페이스를 갖는 것은 실제로 OO 모범 사례를 따르는 것이 아닙니다.

나와 다른 모든 사람들은 때때로 # 1을 무시합니다. 그렇게하려는 경우 :

2) 개인 생성자 가있는 최종 클래스 이것은 적어도 상수에 쉽게 접근 할 수 있도록 확장 / 구현함으로써 누군가가 당신의 '상수 가방'을 남용하는 것을 막을 것입니다. (당신이 이것을하지 않겠다고 말한 것을 압니다.하지만 당신이 그렇게하지 않을 사람이 따라 오는 것은 아닙니다)

3) interface 이것은 작동하지만 # 2에서 가능한 남용 언급을 제공하는 선호하는 것은 아닙니다.

일반적으로 이것이 상수이기 때문에 정상적인 oo 원칙을 적용해서는 안된다는 의미는 아닙니다. 하나의 클래스 만 상수에 대해 신경 쓰지 않는 경우-해당 클래스에 비공개 여야합니다. 테스트 만 상수에 관심이 있다면 프로덕션 코드가 아닌 테스트 클래스에 있어야합니다. 상수가 여러 위치에서 정의 된 경우 (실수로 동일하지 않음)-리팩터링하여 중복을 제거합니다. 등등-당신이 방법처럼 그들을 대하십시오.


2
1의 문제는 때때로 상수를 가져 오기 위해 다른 계층 클래스에 일부 종속성을 코드에 주입한다는 것입니다.
amdev

인터페이스의 모든 필드는 암시 적으로, 공공 정적 최종입니다
Kodebetter

@Kodebetter 그러나 인터페이스를 확장 / 구현하여 더 많은 필드를 추가 할 수 있습니다.
위트

12

Joshua Bloch는 Effective Java에서 다음과 같이 말합니다.

  • 인터페이스는 유형을 정의하는 데만 사용해야합니다.
  • 추상 클래스는 불안정성을 방지하지 않습니다 (하위 클래스 화 될 수 있으며 서브 클래스 화되도록 설계되었음을 제안 할 수도 있음).

모든 상수가 관련되어있는 경우 (예 : 행성 이름) Enum을 사용하거나, 관련된 클래스에 상수 값을 넣거나 (액세스 권한이있는 경우), 비 인스턴트 가능한 유틸리티 클래스 (개인 기본 생성자 정의)를 사용할 수 있습니다. .

class SomeConstants
{
    // Prevents instanciation of myself and my subclasses
    private SomeConstants() {}

    public final static String TOTO = "toto";
    public final static Integer TEN = 10;
    //...
}

그런 다음 이미 언급했듯이 정적 가져 오기를 사용하여 상수를 사용할 수 있습니다.


7

내가 선호하는 방법은 전혀하지 않는 것입니다. 상수의 시대는 Java 5가 typesafe enum을 도입했을 때 거의 죽었습니다. 그리고 그 전에도 Josh Bloch는 Java 1.4 (및 그 이전 버전)에서 작동하는 (약간 더 장황한) 버전을 발표했습니다.

레거시 코드와의 상호 운용성이 필요하지 않는 한 더 이상 명명 된 문자열 / 정수 상수를 사용할 이유가 없습니다.


2
좋은 대답, 예가 더 좋을 것입니다.
amdev 2013

3

마지막 수업을 사용하십시오.

다른 값을 추가하려면 추상 클래스를 사용하십시오.

인터페이스를 사용하는 것은 의미가 없으며 인터페이스는 계약을 지정해야합니다. 상수 값을 선언하고 싶을뿐입니다.



3

enums 괜찮습니다. 효과적인 Java (2nd Ed)의 한 항목 인 IIRC에는 모든 값에 대해 enum[Java 키워드] interface를 구현하는 표준 옵션을 열거하는 상수가 있습니다 .

내가 선호 interface하는 final class것은 상수 에 대해 [Java 키워드] 를 사용하는 것 입니다. 암시 적으로 public static final. 어떤 사람들은를 interface통해 나쁜 프로그래머가 그것을 구현할 수 있다고 주장 할 것입니다.하지만 나쁜 프로그래머는 당신이 무엇을하든 짜증나는 코드를 작성할 것입니다.

어느 쪽이 더 좋을까요?

public final class SomeStuff {
     private SomeStuff() {
         throw new Error();
     }
     public static final String SOME_CONST = "Some value or another, I don't know.";
}

또는:

public interface SomeStuff {
     String SOME_CONST = "Some value or another, I don't know.";
}

하지만 나쁜 프로그래머는 당신이 무엇을하든 짜증나는 코드를 작성할 것입니다. 그것은 사실입니다. 잘못된 프로그래밍을 완화하기위한 조치를 취하거나 완전히 제거 할 수 있다면 그렇게해야 할 약간의 책임이 있습니다. 가능한 한 명시 적이어야합니다. 나쁜 프로그래머는 최종 클래스를 확장 할 수 없습니다.
liltitus27 2011

1
@ liltitus27 어느 정도 동의합니다. 그러나 당신은 정말 나쁜 프로그래머가 나쁜 코드를 작성하는 것을 막기 위해 추악한 코드를 원합니까? 그들이 생산하는 코드는 여전히 절망적이지만 이제는 코드가 읽기 어렵습니다.
Tom Hawtin-tackline

1

또는 4. 상수를 가장 많이 사용하는 논리를 포함하는 클래스에 넣으십시오.

... 미안해, 저항 할 수 없었다 ;-)


3
이것은 좋은 생각이 아닙니다. 이로 인해 아무것도 없어야하는 클래스간에 종속성이 생성됩니다.
Yuval Adam

왜 안돼? 동일한 상수를 사용하는 클래스는 어쨌든 종속성이 있다고 말하고 싶습니다. 그리고 어쨌든이 상수에 가장 많이 의존하는 클래스가 하나 있어야합니다.
Simon Groenewolt

상수를 사용하는 클래스가 하나만 있다면 이것은 의미가 있습니다.
Tom Hawtin-tackline

-1
  1. 개인 생성자의 단점 중 하나는 메서드를 테스트 할 수 없다는 것입니다.

  2. 특정 도메인 유형에 적용하기에 좋은 성격 개념으로 열거, 분산 상수에 적용하기에는 충분하지 않습니다.

Enum의 개념은 "열거는 밀접하게 관련된 항목의 집합입니다"입니다.

  1. 상수 인터페이스를 확장 / 구현하는 것은 나쁜 습관입니다. 직접 참조하는 대신 불변 상수를 확장해야한다는 요구 사항을 생각하기가 어렵습니다.

  2. SonarSource와 같은 품질 도구를 적용하면 개발자가 상수 인터페이스를 삭제하도록 강제하는 규칙이 있습니다. 많은 프로젝트가 상수 인터페이스를 즐기고 상수 인터페이스에서 "확장"일이 거의 발생하지 않기 때문에 이것은 어색한 일입니다.


1
"개인 생성자는 테스트 할 수없는 메서드의 존재입니다.": 참이 아닙니다. 당신 (또는 당신의 상사)이 100 % 코드 커버리지 보고서에 대해 정말로 편집증 적이라면, 리플렉션을 사용하고 생성자가 예외를 던지는 지 확인함으로써 이것을 테스트 할 수 있습니다. 그러나 IMHO, 이것은 형식 일 뿐이며 실제로 테스트 할 필요가 없습니다. 당신 (또는 팀)이 정말로 중요한 것에 만 관심이 있다면 100 % 라인 커버리지가 필요하지 않습니다.
L. Holanda
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.