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
우리는 또한 가지고 addExactmultiplyExact. 분할 ( 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.