Java에서 long to int로 안전하게 캐스팅


489

에서 주조 있는지 확인하기 위해 자바에서 가장 관용적 인 방법은 무엇 long에이 int정보를 잃지 않는다는?

이것은 내 현재 구현입니다.

public static int safeLongToInt(long l) {
    int i = (int)l;
    if ((long)i != l) {
        throw new IllegalArgumentException(l + " cannot be cast to int without changing its value.");
    }
    return i;
}

34
두 개의 코드 경로 하나는 레거시이며 정수가 필요합니다. 그 레거시 데이터는 모두 int에 맞아야하지만 그 가정을 위반하면 예외를 던지고 싶습니다. 다른 코드 경로는 long을 사용하며 캐스트가 필요하지 않습니다.
— Brigham

197
나는 사람들이 왜 당신이하고 싶은 일을하고 싶은지 항상 질문하는 것을 좋아합니다. 모든 사람이이 질문에서 자신의 전체 사용 사례를 설명했다면 아무도 읽을 수 없으며 대답은 훨씬 적습니다.
— BT

24
BT-나는 이런 이유로 온라인으로 질문하는 것을 정말로 싫어합니다. 당신이 도움을 원한다면, 그것은 훌륭하지만 20 개의 질문을하지 말고 스스로 정당화하도록 강요하십시오.
— Mason240

59
BT와 Mason240에 동의하지 않는 경우 : 질문자에게 생각하지 못한 다른 솔루션을 제시하는 것이 종종 가치가 있습니다. 코드 냄새를 신고하는 것은 유용한 서비스입니다. ‘왜 ...에 대해 궁금합니다.’에서‘자신을 정당화하도록 강요하는 것’에서 먼 길입니다.
— Tommy Herbert

13
배열 인덱스와 같이 long으로 할 수없는 일이 많이 있습니다.
— skot

답변:


580

이를 위해 Java 8 에 새로운 방법이 추가되었습니다 .

import static java.lang.Math.toIntExact;

long foo = 10L;
int bar = toIntExact(foo);

ArithmeticException오버플 로 가 발생 하면 던질 것 입니다.

보다: Math.toIntExact(long)

몇 가지 다른 오버 플로우 안전한 방법은 그들로 끝 자바 8에 추가 된 정확한 .

예 :

  • Math.incrementExact(long)
  • Math.subtractExact(long, long)
  • Math.decrementExact(long)
  • Math.negateExact(long),
  • Math.subtractExact(int, int)

5
우리는 또한 가지고 addExact와 multiplyExact. 분할 ( MIN_VALUE/-1)과 절대 값 ( abs(MIN_VALUE))에는 안전한 편의 방법이 없습니다.
— Aleksandr Dubinsky

그러나 Math.toIntExact()일반적인 캐스트 대신에 사용하는 것의 차이점은 무엇 int입니까? 의 구현은 Math.toIntExact()에 캐스트 long됩니다 int.
— Yamashiro Rion

@YamashiroRion 실제로 toIntExact의 구현은 먼저 캐스트가 오버플로를 유발하는지 확인합니다.이 경우 ArithmeticException이 발생합니다. 캐스트가 안전한 경우에만 long에서 int로 캐스트를 수행하여 반환합니다. 즉, int로 표현할 수없는 긴 숫자 (예 : 2 147 483 647보다 큰 숫자)를 캐스트하려고하면 ArithmeticException이 발생합니다. 간단한 캐스트로도 동일한 작업을 수행하면 결과 int 값이 잘못됩니다.
— Pierre-Antoine

306

나는 다음과 같이 간단하게 할 것이라고 생각합니다.

public static int safeLongToInt(long l) {
    if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
        throw new IllegalArgumentException
            (l + " cannot be cast to int without changing its value.");
    }
    return (int) l;
}

반복 캐스팅보다 의도를 더 명확하게 표현한다고 생각하지만 다소 주관적입니다.

잠재적 관심에 주목하십시오-C #에서는 다음과 같습니다.

return checked ((int) l);

7
나는 항상 범위 검사를로 수행했다 (!(Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE)). 나는 다른 방법으로 머리를 쓰는 것이 어렵다는 것을 알게되었습니다. 동정 자바는 없습니다 unless.
— Tom Hawtin-tackline

5
+1. 이는 "예외가 예외적 인 조건에 사용되어야합니다 "규칙에 정확히 해당합니다 .
— Adam Rosenfield

4
(현대 범용 언어에서는 "Eh? 그러나 ints는 임의의 크기를
— 가집

7
@Tom : 개인적 취향, 나는 추측합니다-가능한 한 적은 부정적인 것을 선호합니다. 내가 예외를 던지는 몸체를 가진 "if"를보고 있다면, 값이 "최저"에서 벗어나는 것처럼 예외적으로 보이는 조건을보고 싶다 int.
— Jon Skeet

6
@Tom :이 경우 부정적인 것을 제거하고 캐스트 / 반환을 "if"본문에 넣은 다음 나중에 의미를 알면 예외를 던집니다.
— Jon Skeet

132

Google Guava의 Ints 클래스를 사용하면 메소드를 다음과 같이 변경할 수 있습니다.

public static int safeLongToInt(long l) {
    return Ints.checkedCast(l);
}

연결된 문서에서 :

점검

public static int checkedCast(long value)

value가능한 경우와 동일한 int 값을 반환 합니다.

매개 변수 : value - int유형 범위의 모든 값

반환 값 :int 값 같value

예외 : IllegalArgumentException - value이보다 크 Integer.MAX_VALUE거나 작은 경우Integer.MIN_VALUE

또한 safeLongToInt광범위한 리팩토링없이 기능을 변경하기 위해 래퍼를 남겨 두지 않는 한 래퍼 가 필요하지 않습니다 .


3
구아바 Ints.checkedCast는 OP
— 부분적으로 흐린

14
Guava 솔루션의 경우 +1이지만 다른 방법으로 랩핑 할 필요는 없지만 Ints.checkedCast(l)직접 호출하면 됩니다.
— dimo414

8
구아바는 Ints.saturatedCast예외를 던지는 대신 가장 가까운 값을 반환합니다.
— Jake Walsh

예, 기존 API를 내 경우로 사용하는 것이 안전합니다. 이미 이미 라이브러리에있는 라이브러리 : 유효하지 않은 경우 예외 발생 : Ints.checkedCast (long) 및 Ints.saturatedCast (long) long을 int로 변환하기 위해 가장 가까운 값을 얻으십시오.
— Osify

29

BigDecimal로 :

long aLong = ...;
int anInt = new BigDecimal(aLong).intValueExact(); // throws ArithmeticException
                                                   // if outside bounds

나는 이것을 좋아한다. 누구 든지이 솔루션에 대해 뭔가가 있습니까?
— Rui Marques

12
글쎄, 그것은 유틸리티 메소드가 무엇인지 알아 내기 위해 BigDecimal을 할당하고 버리고 있습니다. 그래서 그것은 최선의 프로세스가 아닙니다.
— Riking

@Riking에서는 새 인스턴스가 필요하지 않음을 나타내는 BigDecimal.valueOf(aLong)대신 을 사용하는 것이 좋습니다 new BigDecimal(aLong). 실행 환경이 해당 메소드에서 캐싱하는지 여부는 이스케이프 분석의 존재와 마찬가지로 구현마다 다릅니다. 대부분의 실제 상황에서 이것은 성능에 영향을 미치지 않습니다.
— Holger

17

여기에 필요한 것보다 큰 경우 가치에 신경 쓰지 않는 경우 해결책이 있습니다.)

public static int safeLongToInt(long l) {
    return (int) Math.max(Math.min(Integer.MAX_VALUE, l), Integer.MIN_VALUE);
}

당신은 틀린 것 같습니다 ... 그것은 좋은 다음 부정적인 작동합니다. 또한 무엇을 의미 too low합니까? 유스 케이스를 제공하십시오.
— Vitaliy Kulikov

이 솔루션은 빠르고 안전한 솔루션이므로 결과에 따르기 위해 Long to Int를 캐스팅하려고합니다.
— Vitaliy Kulikov

11

DONT : 이것은 해결책이 아닙니다!

내 첫 번째 접근 방식은 다음과 같습니다.

public int longToInt(long theLongOne) {
  return Long.valueOf(theLongOne).intValue();
}

그러나 그것은 단지 long을 int로 캐스팅하여 잠재적으로 새로운 Long인스턴스를 생성 하거나 Long 풀에서 검색 할 수 있습니다.


단점

  1. Long.valueOfLong숫자가 Long풀 범위 [-128, 127] 내에없는 경우 새 인스턴스를 만듭니다 .

  2. intValue구현은 다음을 수행합니다.

    return (int)value;

따라서 이것은 longto에 캐스팅하는 것보다 더 나쁜 것으로 간주 될 수 있습니다 int.


4
도와 주려는 노력에 감사하지만, 효과가없는 것에 대한 예를 제시하는 것은 효과가있는 솔루션을 제공하는 것과는 다릅니다. 올바른 방법을 추가하기 위해 편집한다면, 이것은 꽤 좋을 것입니다; 그렇지 않으면 답변으로 게시하는 것이 적합하지 않습니다.
— Pops

4
좋아, 왜 DO와 DONT를 가지고 있지 않은가? Tbh, 때로는 그런 패턴 / 코드를 사용했는지 확인하기 위해 수행하지 않는 방법 (DONT) 목록이 있었으면합니다. 어쨌든이 "답변"을 삭제할 수 있습니다.
— Andreas

1
좋은 안티 패턴. 어쨌든 long 값이 int 범위를 벗어나면 어떻게되는지 설명하면 좋을 것입니까? ClassCastException 또는 이와 유사한 것이있을 것이라고 생각합니까?
— Peter Wippermann

2
@ PeterWippermann : 더 많은 정보를 추가했습니다. 당신은 그들에게 이해할 수있는 대답이라고 생각합니까? 충분한 설명?
— Andreas

7

값을 캐스팅하면 값이 변경되었는지 확인하는 확실한 방법은 캐스팅하고 결과를 확인하는 것입니다. 그러나 비교할 때 불필요한 캐스트를 제거합니다. 나는 또한 너무 한 문자 변수 이름에 예리한 (예외 아니에요 x하고 y있지만 때 평균 행과 열 (때로는 각각)).

public static int intValue(long value) {
    int valueInt = (int)value;
    if (valueInt != value) {
        throw new IllegalArgumentException(
            "The long value "+value+" is not within range of the int type"
        );
    }
    return valueInt;
}

그러나 실제로 가능한 경우이 변환을 피하고 싶습니다. 분명히 가능하지는 않지만 IllegalArgumentException클라이언트 코드와 관련하여 예외가 발생하는 것은 거의 확실합니다.


1
이것이 Google Guava Ints :: checkedCast의 최신 버전입니다.
— lexicalscope

2

Java 정수 유형은 부호있는 것으로 표시됩니다. 2 31 ~ 2 32 (또는 -2 31 ~ -2 32 ) 사이의 입력을 사용하면 캐스트는 성공하지만 테스트는 실패합니다.

확인해야 할 것은 모든 상위 비트 long가 모두 같은지 여부입니다.

public static final long LONG_HIGH_BITS = 0xFFFFFFFF80000000L;
public static int safeLongToInt(long l) {
    if ((l & LONG_HIGH_BITS) == 0 || (l & LONG_HIGH_BITS) == LONG_HIGH_BITS) {
        return (int) l;
    } else {
        throw new IllegalArgumentException("...");
    }
}

3
서명과 관련이 없는지 모르겠습니다. 당신은 예를 들어 줄래 하지 않습니다 잃게 정보 만 수행 테스트 실패를? 2 ^ 31은 Integer.MIN_VALUE (예 : -2 ^ 31)로 캐스트되므로 정보가 유실되었습니다.
— Jon Skeet

@ Jon Skeet : 아마도 나와 OP가 서로 이야기하고있을 것입니다. (int) 0xFFFFFFFF그리고 (long) 0xFFFFFFFFL다른 값을 가지고 있지만 모두 같은 "정보"를 포함하고는 INT에서 원래 긴 값을 추출 거의 간단하다.
— mob

long이 0xFFFFFFFF 대신에 -1로 시작했을 때 int에서 원래 long 값을 어떻게 추출 할 수 있습니까?
— Jon Skeet

명확하지 않으면 죄송합니다. long과 int에 동일한 32 비트 정보가 포함되어 있고 32 비트가 설정된 경우 int 값이 long 값과 다르지만 긴 가치를 얻는 것은 쉽다
— mob

@mob 이것은 무엇을 의미합니까? OP의 코드는 2 ^ {31}보다 긴 값을 정수로 캐스트 할 수 없다고 올바르게보고합니다.
— 부분적으로 흐린

0
(int) (longType + 0)

그러나 Long은 최대 값을 초과 할 수 없습니다 :)


1
은 + 0그것이 당신이 아무 이유없이 추가 작업을 수행하지 않기 때문에 자바 문자열 유사한 방식으로 숫자 유형의 연결을 처리하는 경우, 그것이 작동 할 수 있습니다이 변환에 아무것도 추가하지 않습니다 만.
— sixones

-7

다른 솔루션은 다음과 같습니다.

public int longToInt(Long longVariable)
{
    try { 
            return Integer.valueOf(longVariable.toString()); 
        } catch(IllegalArgumentException e) { 
               Log.e(e.printstackstrace()); 
        }
}

클라이언트가 POST를 수행하고 클라이언트가 Long을 가지고있는 동안 서버 DB가 정수 만 이해하는 경우에 이것을 시도했습니다.


: 당신은 "진짜 오래"값에 NumberFormatException이 얻을 것이다 Integer.valueOf(Long.MAX_VALUE.toString()); 결과 java.lang.NumberFormatException: For input string: "9223372036854775807" 가 지금 문자가 포함 된 문자열을 처리하는 것과 동일한 방식으로 처리되기 때문에 거의 범위를 벗어난-예외를 모호하게하는.
— Andreas

2
또한 항상 값을 반환하지는 않기 때문에 컴파일되지 않습니다.
— Patrick M
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.