web-dev-qa-db-ja.com

安全にJavaにintをキャストする

longからintへのキャストで情報が失われないことを確認するための、Javaで最も慣用的な方法は何ですか?

これが私の現在の実装です。

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;
}
451
Brigham

そうするために、新しいメソッドが Java 8 で追加されました。

import static Java.lang.Math.toIntExact;

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

オーバーフローの場合はArithmeticExceptionをスローします。

参照してください: Math.toIntExact(long)

Java 8には、他にもいくつかのオーバーフローセーフメソッドが追加されています。それらは、exactで終わります。

例:

  • Math.incrementExact(long)
  • Math.subtractExact(long, long)
  • Math.decrementExact(long)
  • Math.negateExact(long),
  • Math.subtractExact(int, int)
525
Pierre-Antoine

私は私がそれをするのと同じくらい単純にそれをすると思います:

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);
302
Jon Skeet

Google Guavaの Ints クラスを使用すると、メソッドを次のように変更できます。

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

リンクされたドキュメントから:

checkedCast

public static int checkedCast(long value)

可能であれば、valueと等しいint値を返します。

パラメーター: value - int型の範囲内の任意の値

戻り値: intと等しいvalue

例外: IllegalArgumentException - valueInteger.MAX_VALUEより大きい、またはInteger.MIN_VALUEより小さい場合

ちなみに、safeLongToIntラッパーは、もちろん大規模なリファクタリングを行わずに機能を変更するために残しておきたいのでなければ必要ありません。

128
prasopes

BigDecimalの場合:

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

それが必要とされるよりも大きい場合にあなたが価値を気にしない場合のために、ここに解決策があります;)

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

いけません:これは解決策ではありません!

私の最初のアプローチは次のとおりです。

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

しかし、それは単にlongをintにキャストするだけで、潜在的に新しいLongインスタンスを作成するか、またはLongプールからそれらを取得します。


欠点

  1. 数値がLongのプール範囲内にない場合、Long.valueOfは新しいLongインスタンスを作成します[-128、127]。

  2. intValueの実装は以下のことしかしません:

    return (int)value;
    

したがって、これは単にlongintにキャストするよりもさらに悪いと考えることができます。

11
Andreas

値をキャストして値が変わったかどうかを確認する明らかな方法は、キャストして結果を確認することであると私は主張します。ただし、比較するときは、不要なキャストを削除します。私はまた、一文字の変数名にもそれほど熱心ではありません(例外xy

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はクライアントコードに関する限り投げているのはほぼ間違いなく間違った例外です。

Java整数型は符号付きとして表されます。 2の間の入力で31 と232 (または-231 と-232)キャストは成功しますが、テストは失敗します。

確認する必要があるのは、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("...");
    }
}
2
mob
(int) (longType + 0)

しかし、Longは最大値を超えることはできません:)

0
Maury