Java에서 원시 클래스와 클래스를 언제 사용합니까?


54

Java에는 부울 (클래스) 대 부울 (기본)이 있습니다. 마찬가지로 정수 (클래스) 대 int (기본)가 있습니다. 기본 버전과 클래스를 사용하는시기에 대한 모범 사례는 무엇입니까? 특정 (성능?) 이유가없는 한 기본적으로 항상 클래스 버전을 사용해야합니까? 가장 일반적으로 사용되는 방법은 무엇입니까?



내 생각에 그것들은 수업이 아니며 상자입니다. 그것들은 단지 거기에 있으므로 객체가 필요한 곳, 즉 컬렉션에서 프리미티브를 사용할 수 있습니다. 정수를 두 개 추가 할 수 없습니다 (가장 척할 수는 있지만 실제로 java는 자동으로 값을 개봉합니다).
stonemetal

답변:


47

Joshua Bloch는 효과적인 Java의 항목 5에서

교훈은 분명하다 : 박스형 프리미티브보다 프리미티브를 선호하고 의도하지 않은 오토 박싱에주의한다 .

클래스의 일반적인 용도는 클래스를 일반 유형 (목록 및 맵과 같은 Collection 클래스 포함)으로 사용하거나 암시 적 캐스트없이 다른 유형으로 변환하려는 경우입니다 (예 : Integerclass에는 메소드 doubleValue()또는) byteValue().

편집 : Joshua Bloch의 이유는 다음과 같습니다.

// Hideously slow program! Can you spot the object creation?
public static void main(String[] args) {
    Long sum = 0L;
    for (long i = 0; i < Integer.MAX_VALUE; i++) {
         sum += i;
    }
    System.out.println(sum);
}

이 프로그램은 정답을 얻지 만 한 글자 인쇄상의 오류로 인해 예상보다 훨씬 느립니다. 변수 sum는 a Long대신에 선언됩니다. long즉, 프로그램은 약 2 ^ 31 개의 불필요한 Long인스턴스를 구성합니다 (대개 long i에 추가 될 때마다 대략 1 개씩 Long sum). 합계 선언을에서 Long로 변경 long하면 컴퓨터에서 런타임이 43 초에서 6.8 초로 줄어 듭니다.


2
그의 결론을 인용하기보다는 블로흐의 이유를 열거하면 도움이 될 것입니다!
vaughandroid

@Baqueta 게시물을 편집했습니다. 그 이유는 성능입니다.
m3th0dman

조금 더 명확합니다. 감사합니다. 나는 지금 내 자신의 답변을 게시하는 것이 정당하다고 생각합니다. :)
vaughandroid

당신은 lmax 아키텍처가 흥미로울 것입니다. "다른 수준으로 올라가려면 약간 더 영리했습니다. LMAX 팀이 거기에 도달하는 데 도움이되는 몇 가지가 있습니다. 하나는 디자인 된 Java 컬렉션의 사용자 정의 구현을 작성하는 것이 었습니다 캐시에 친숙하고 가비지에주의해야합니다. 예를 들어 기본 자바 long 을 특수하게 작성된 배열 지원 Map 구현과 함께 해시 맵 키로 사용하는 것 입니다. "

JIT가 처리해야 할 것 같습니다
deFreitas

28

일반적인 방법은 제네릭을 다루지 않는 한 기본 요소를 사용하는 것입니다 (자동 상자 및 상자 해제에 대해 알고 있어야합니다 !).

이 협약을 따라야하는 여러 가지 이유가 있습니다.

1. 간단한 실수를 피하십시오.

초보자를 종종 사로 잡는 미묘하고 직관적이지 않은 경우가 있습니다. 숙련 된 코더조차도 실수를해서 실수를 저지르기도합니다 (코드를 디버깅하고 오류를 찾을 때 맹세합니다!).

가장 일반적인 실수는 a == b대신을 사용하는 것입니다 a.equals(b). 사람들은 a == b프리미티브 작업에 익숙 하므로 Object 래퍼를 사용할 때 쉽게 수행 할 수 있습니다.

Integer a = new Integer(2);
Integer b = new Integer(2);
if (a == b) { // Should be a.equals(b)
    // This never gets executed.
}
Integer c = Integer.valueOf(2);
Integer d = Integer.valueOf(2);
if (c == d) { // Should be a.equals(b), but happens to work with these particular values!
    // This will get executed
}
Integer e = 1000;
Integer f = 1000;
if (e == f) { // Should be a.equals(b)
    // Whether this gets executed depends on which compiler you use!
}

2. 가독성 :

다음 두 가지 예를 고려하십시오. 대부분의 사람들은 두 번째가 더 읽기 쉽다고 말합니다.

Integer a = 2;
Integer b = 2;
if (!a.equals(b)) {
    // ...
}
int c = 2;
int d = 2;
if (c != d) {
    // ...
}

3. 성능 :

사실 프리미티브를 사용하는 것보다 프리미티브에 Object 래퍼를 사용하는 것이 속도 느립니다. 객체 인스턴스화, 메소드 호출 등의 비용을 모든 곳에서 사용하는 것에 추가하고 있습니다 .

Knuth의 "... 시간의 약 97 % : 조기 최적화는 모든 악의 근원"이라는 말은 실제로 적용되지 않습니다. 그는 코드 (또는 시스템)를 더 복잡하게 만드는 최적화에 대해 이야기했습니다. 포인트 # 2에 동의하면 코드가 복잡해집니다.

4. 컨벤션입니다.

다른 Java 프로그래머의 99 %와 다른 스타일을 선택하면 두 가지 단점이 있습니다.

  • 다른 사람의 코드를 읽기가 더 어렵다는 것을 알게 될 것입니다. 예제 / 자습서 등의 99 %가 기본 요소를 사용합니다. 당신이 그것을 읽을 때마다 당신은 그것이 익숙한 스타일로 어떻게 보일지에 대한 생각에 대한인지 적 오버 헤드를 갖게 될 것입니다.
  • 다른 사람들은 코드를 읽기가 더 어렵다는 것을 알게 될 것입니다. Stack Overflow에 대해 질문 할 때마다 "왜 프리미티브를 사용하지 않습니까?"라는 답변 / 설명을 살펴보아야합니다. 당신이 나를 믿지 않는다면, 사람들이 대괄호 배치와 같은 것들에 대한 전투를 살펴보십시오.이 코드는 생성 된 코드에도 영향을 미치지 않습니다!

일반적으로 나는 몇 가지 반점을 열거하지만, 나는 여기서 대회에 참석하지 않는 좋은 이유를 솔직히 생각할 수 없습니다!


2
사람들과 객체를 비교하도록 제안하지 마십시오 ==. 객체는와 비교해야합니다 equals().
Tulains Córdova

2
@ user61852 나는 그것을 할 일로 제안하는 것이 아니라 일반적인 실수로 제안했습니다! 좀 더 명확하게해야합니까?
vaughandroid

예, 대상과 비교할 필요는 없습니다. equals()대상과 비교 ==하면 예상 결과를 얻을 수 있습니다.
Tulains Córdova

좋은 지적. 그것을 바꿨다.
vaughandroid

equals()두 번째 코드 스 니펫에 추가 하고 투표를 변경했습니다.
Tulains Córdova

12

보통 나는 프리미티브와 함께 간다. 그러나, 같은 클래스를 사용하여 하나의 특질 IntegerBoolean할당의 가능성 null이 변수에이. 물론 이것은 null항상 검사 를 수행해야 하지만 올바르게 초기화되지 않은 일부 int또는 boolean변수 를 사용하여 논리 오류가 발생하는 것보다 NullPointerException을 얻는 것이 좋습니다 .

물론, Java 8 이후로 한 단계 더 발전 Integer할 수 있습니다 ( 예 : Optional<Integer>값이 있거나없는 변수에 사용할 수 있음).

또한 null해당 변수에 " 알 수없는 "또는 " 와일드 카드 "값 을 할당하는 데 사용할 수 있는 가능성을 소개합니다 . 이는 Ternary Logic 과 같은 일부 상황에서 유용 할 수 있습니다 . 또는 특정 개체가 일부 템플릿과 일치하는지 확인하고 싶을 수도 있습니다. 이 경우 null개체의 값을 가질 수있는 템플릿의 변수에 사용할 수 있습니다.


2
(내 다운 보트는 아니지만 ...) Java는 이미 초기화되지 않은 변수를 감지했으며 사용으로 이어지는 모든 코드 경로에 값이 ​​명확하게 지정 될 때까지 변수를 읽을 수 없습니다. 따라서 null기본값 으로 할당 할 수있는 능력에서 많은 것을 얻지 못합니다 . 반대로 : 변수를 "초기화"하지 않는 것이 좋습니다. 기본값을 null설정해도 컴파일러가 종료되지만 모든 코드 경로 에 유용한 할당 이 부족하다는 것을 감지하지 못합니다 . 따라서 컴파일러가 잡을 수있는 오류가 런타임으로 빠져 나갑니다.
cHao

@cHao 그러나 변수를 초기화하기 위해 합리적인 기본값 이 없다면 ? 0.0, 또는 -1, 또는 Integer.MAX_VALUE, 또는로 설정할 수 False있지만 결국 기본값인지 또는 해당 변수에 지정된 실제 값인지 알 수 없습니다. 이것이 중요한 경우, null가치가 더 명확 할 수 있습니다.
tobias_k

명확하지는 않지만 버그가 이미 전파 될 때까지 불명확 한 논리에 대해 경고하지 않도록 컴파일러에 지시하는 것이 더 쉽습니다. : P 합리적인 기본값 이없는 경우 변수를 초기화하지 마십시오. 합리적인 가치가있을 때만 설정하십시오. 이렇게하면 값이 올바르게 설정되지 않은 경우 Java 가 컴파일시 중지 됩니다.
cHao

@cHao 나는 변수를 초기화 할 수없고 런타임에 변수 처리 해야하는 경우가있을 수 있음을 의미 했습니다. 이러한 경우, "null"과 같은 "default"는 기본값으로 명확하게 식별 될 수 있거나 "초기화되지 않은"것이 유효한 값일 수있는 컴파일 타임 초기화보다 낫습니다.
tobias_k

그런 경우를 염두에두고 있습니까? 내가 생각할 수있는 경우는 인터페이스 경계에 있습니다 (예 : 매개 변수 또는 반환 유형). 그러나 거기에서도 랩핑하면 훨씬 더 좋습니다. (Naked null에는 null paranoia를 포함하여 많은 문제가 있습니다.) 함수 내에서 실제로 사용 시점에서 초기화되지 않은 변수는 일반적으로 발견되지 않은 경우를 나타냅니다. (확정 할당 분석은 단순하므로 오 탐지가 가능합니다. 그러나 논리를 단순화하여이를 해결할 수도 있습니다.)
cHao

2

평신도의 말로 :

컬렉션에 항목을 추가해야 할 때 래퍼를 사용합니다.

컬렉션은 기본 요소를 보유 할 수 없습니다.


0

자바는 m3th0dman이 지적한대로 오토 박스 기능을 갖추고 있습니다. 가능한 가장 낮은 수준에서 생각해 보면 오토 박싱 (in 또는 out) 프리미티브 값이 애플리케이션 주변의 기본 데이터 유형으로 작업하는 경우 필요하지 않은 일부 작업에서 소비되는 클럭 사이클을 의미한다는 것을 알 수 있습니다.

일반적으로 가능할 때마다 기본 데이터 유형을 사용해야합니다.

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