Konwersja BigDecimal na Integer

136

Mam metodę Hibernate, która zwraca mi BigDecimal. Mam inną metodę API, do której muszę przekazać ten numer, ale akceptuje ona liczbę całkowitą jako parametr. Nie mogę zmienić typów zwracanych ani typów zmiennych obu metod.

Teraz jak przekonwertować BigDecimal na Integer i przekazać go do drugiej metody?

Czy jest wyjście z tego?

Vishal
źródło
6
Poprawiłem twój tytuł. To jest konwersja, a nie rzucanie.
Markiz Lorne

Odpowiedzi:

214

Zadzwoniłbyś myBigDecimal.intValueExact()(lub po prostu intValue()), a nawet wyrzuci wyjątek, jeśli stracisz informacje. To zwraca int, ale zajmuje się tym autoboxing.

willcodejavaforfood
źródło
1
intValueExact()to dobra rekomendacja; został dodany w 1.5
Anon
Sprawdzanie intValue()jest niebezpieczne, chyba że stawiasz na 100% swoje życie, mając pewność, że numer mieści się w Integerzakresie.
Snekse
1
Czy to ma sens: „Integer.valueOf (myBigDecimal.intValueExact ())”?
OddDev
33

Czy możesz zagwarantować, że BigDecimalnigdy nie będzie zawierał wartości większej niż Integer.MAX_VALUE?

Jeśli tak, to dzwoni tutaj kod intValue:

Integer.valueOf(bdValue.intValue())
Zaraz
źródło
Tak. Myślę, że nie będzie zawierał większej wartości niż Integer.MAX_VALUE
Vishal
2
Nie ma innego powodu, aby wykonać wartość valueOf, niż uzyskać Object zamiast prymitywu, prawda?
Basil
Powiedziałbym, że to powinna być oficjalna odpowiedź, ponieważ plik. wymienia ograniczenia, b. zapobiega autoboxingowi.
ledlogic
22

TL; DR

Użyj jednego z nich dla potrzeb powszechnej konwersji

//Java 7 or below
bigDecimal.setScale(0, RoundingMode.DOWN).intValueExact()
//Java 8    
bigDecimal.toBigInteger().intValueExact()

Rozumowanie

Odpowiedź zależy od wymagań i od tego, jak odpowiesz na to pytanie.

  • Czy BigDecimalpotencjalnie będzie miał niezerową część ułamkową?
  • Czy BigDecimalpotencjalnie nie będzie pasować do Integerzakresu?
  • Czy chciałbyś, aby niezerowe części ułamkowe były zaokrąglane lub obcięte?
  • Czy chciałbyś, aby niezerowe części ułamkowe były zaokrąglane?

Jeśli odpowiedziałeś nie na pierwsze 2 pytania, możesz po prostu użyć BigDecimal.intValueExact()tego, co sugerowali inni i pozwolić, aby wybuchło, gdy wydarzy się coś nieoczekiwanego.

Jeśli nie masz 100% pewności co do pytania numer 2, zawszeintValue() jest to zła odpowiedź.

Robię to lepiej

Skorzystajmy z następujących założeń na podstawie innych odpowiedzi.

  • Nie przeszkadza nam utrata precyzji i obcięcie wartości, ponieważ to właśnie robią intValueExact()auto-boks
  • Chcemy, aby wyjątek był wyrzucany, gdy BigDecimaljest większy niż Integerzakres, ponieważ wszystko inne byłoby szalone, chyba że masz bardzo konkretną potrzebę zawijania, co dzieje się, gdy upuszczasz bity o wyższym porządku.

Biorąc pod uwagę te parametry, intValueExact()zgłasza wyjątek, gdy nie chcemy, aby nasza część ułamkowa była różna od zera. Z drugiej strony intValue()nie zgłasza wyjątku, gdy powinien, jeśli nasz BigDecimaljest zbyt duży.

Aby uzyskać to, co najlepsze z obu światów, zaokrąglij BigDecimalpierwszy, a następnie przekonwertuj. Ma to również tę zaletę, że zapewnia większą kontrolę nad procesem zaokrąglania.

Spock Groovy Test

void 'test BigDecimal rounding'() {
    given:
    BigDecimal decimal = new BigDecimal(Integer.MAX_VALUE - 1.99)
    BigDecimal hugeDecimal = new BigDecimal(Integer.MAX_VALUE + 1.99)
    BigDecimal reallyHuge = new BigDecimal("10000000000000000000000000000000000000000000000")
    String decimalAsBigIntString = decimal.toBigInteger().toString()
    String hugeDecimalAsBigIntString = hugeDecimal.toBigInteger().toString()
    String reallyHugeAsBigIntString = reallyHuge.toBigInteger().toString()

    expect: 'decimals that can be truncated within Integer range to do so without exception'
    //GOOD: Truncates without exception
    '' + decimal.intValue() == decimalAsBigIntString
    //BAD: Throws ArithmeticException 'Non-zero decimal digits' because we lose information
    // decimal.intValueExact() == decimalAsBigIntString
    //GOOD: Truncates without exception
    '' + decimal.setScale(0, RoundingMode.DOWN).intValueExact() == decimalAsBigIntString

    and: 'truncated decimal that cannot be truncated within Integer range throw conversionOverflow exception'
    //BAD: hugeDecimal.intValue() is -2147483648 instead of 2147483648
    //'' + hugeDecimal.intValue() == hugeDecimalAsBigIntString
    //BAD: Throws ArithmeticException 'Non-zero decimal digits' because we lose information
    //'' + hugeDecimal.intValueExact() == hugeDecimalAsBigIntString
    //GOOD: Throws conversionOverflow ArithmeticException because to large
    //'' + hugeDecimal.setScale(0, RoundingMode.DOWN).intValueExact() == hugeDecimalAsBigIntString

    and: 'truncated decimal that cannot be truncated within Integer range throw conversionOverflow exception'
    //BAD: hugeDecimal.intValue() is 0
    //'' + reallyHuge.intValue() == reallyHugeAsBigIntString
    //GOOD: Throws conversionOverflow ArithmeticException because to large
    //'' + reallyHuge.intValueExact() == reallyHugeAsBigIntString
    //GOOD: Throws conversionOverflow ArithmeticException because to large
    //'' + reallyHuge.setScale(0, RoundingMode.DOWN).intValueExact() == reallyHugeAsBigIntString

    and: 'if using Java 8, BigInteger has intValueExact() just like BigDecimal'
    //decimal.toBigInteger().intValueExact() == decimal.setScale(0, RoundingMode.DOWN).intValueExact()
}
Snekse
źródło
18

Cóż, możesz zadzwonić BigDecimal.intValue():

Konwertuje ten BigDecimal na int. Ta konwersja jest analogiczna do zawężającej konwersji pierwotnej z podwójnego na krótki, jak zdefiniowano w specyfikacji języka Java: każda ułamkowa część tego BigDecimal zostanie odrzucona, a jeśli wynikowa „BigInteger” jest zbyt duża, aby zmieścić się w int, tylko niska -order 32 bity są zwracane. Zauważ, że ta konwersja może utracić informacje o ogólnej wielkości i dokładności tej wartości BigDecimal, a także zwrócić wynik z przeciwnym znakiem.

Następnie możesz jawnie wywołać Integer.valueOf(int)lub pozwolić automatycznemu boksowaniu zrobić to za Ciebie, jeśli używasz wystarczająco aktualnej wersji Javy.

Jon Skeet
źródło
9

Poniższe powinno załatwić sprawę:

BigDecimal d = new BigDecimal(10);
int i = d.intValue();
Gareth Davis
źródło
-4

Okazało się, że powyższe nie działa dla mnie. Wyciągałem wartość komórki z tabeli JTable, ale nie mogłem rzutować na wartość double lub int itp. Moje rozwiązanie:

Object obj = getTable().getValueAt(row, 0);

gdzie wiersz 0 zawsze byłby liczbą. Mam nadzieję, że to pomoże każdemu, kto nadal przewija!

ul
źródło