Gute Möglichkeit, Integer.parseInt () zu kapseln

Ich habe ein Projekt, in dem wir oft Integer.parseInt() , um einen String in einen int zu konvertieren. Wenn etwas schief läuft (zum Beispiel ist der String keine Zahl, sondern der Buchstabe a oder was auch immer), triggers diese Methode eine Ausnahme aus. Wenn ich jedoch Ausnahmen überall in meinem Code behandeln muss, sieht das sehr schnell sehr hässlich aus. Ich möchte dies in eine Methode einfügen, aber ich habe keine Ahnung, wie man einen sauberen Wert zurückgibt, um zu zeigen, dass die Konvertierung falsch gelaufen ist.

In C ++ hätte ich eine Methode erstellen können, die einen pointers auf ein int akzeptiert und die Methode selbst true oder false zurückgeben lässt. Soweit ich weiß, ist dies in Java nicht möglich. Ich könnte auch ein Objekt erstellen, das eine True / False-Variable und den konvertierten Wert enthält, aber dies scheint auch nicht ideal zu sein. Das gleiche gilt für einen globalen Wert, und dies könnte mir beim Multithreading Probleme bereiten.

Gibt es einen sauberen Weg, dies zu tun?

   

Sie könnten eine Integer anstelle einer Integer und bei einem null bei der Syntaxanalyse null .

Es ist eine Schande, dass Java keine Möglichkeit bietet, dies zu tun, ohne dass eine Ausnahme intern ausgetriggers wird – Sie können die Ausnahme ausblenden (indem Sie sie abfangen und null zurückgeben), aber es könnte immer noch ein Performance-Problem sein, wenn Sie Hunderte analysieren von Tausenden von Bits von Benutzern bereitgestellten Daten.

EDIT: Code für eine solche Methode:

 public static Integer tryParse(String text) { try { return Integer.parseInt(text); } catch (NumberFormatException e) { return null; } } 

Beachten Sie, dass ich mir nicht sicher bin, was das bedeutet, wenn der text null ist. Sie sollten bedenken, dass – wenn es sich um einen Fehler handelt (dh Ihr Code kann einen ungültigen Wert übergeben, sollte aber niemals null übergeben), das Auslösen einer Ausnahme angemessen sein; Wenn es sich nicht um einen Fehler handelt, sollten Sie NULL genauso zurückgeben wie für jeden anderen ungültigen Wert.

Ursprünglich verwendete diese Antwort den new Integer(String) -Konstruktor; Es verwendet jetzt Integer.parseInt und eine Box-Operation; Auf diese Weise werden kleine Werte in zwischengespeicherte Integer Objekte Integer , was sie in diesen Situationen effizienter macht.

Welches Verhalten erwarten Sie, wenn es sich nicht um eine Nummer handelt?

Wenn Sie beispielsweise häufig einen Standardwert haben, der verwendet werden soll, wenn die Eingabe keine Zahl ist, könnte eine Methode wie diese hilfreich sein:

 public static int parseWithDefault(String number, int defaultVal) { try { return Integer.parseInt(number); } catch (NumberFormatException e) { return defaultVal; } } 

Ähnliche Methoden können für ein anderes Standardverhalten geschrieben werden, wenn die Eingabe nicht analysiert werden kann.

In einigen Fällen sollten Sie Parsing-Fehler als Fail-Fast-Situationen behandeln, aber in anderen Fällen, wie z. B. bei der Anwendungskonfiguration, verwende ich lieber fehlende Eingaben mit Standardwerten mit Apache Commons Lang 3 NumberUtils .

 int port = NumberUtils.toInt(properties.getProperty("port"), 8080); 

Um zu vermeiden, Ausnahmen zu behandeln, verwenden Sie einen regulären Ausdruck, um sicherzustellen, dass Sie alle Ziffern zuerst haben:

 if(value.matches("\\d+") { Integer.parseInt(value); } 

In Guava gibt es Ints.tryParse() . Es wird keine Ausnahme für eine nicht numerische Zeichenfolge ausgetriggers, jedoch wird die Ausnahme für die Nullzeichenfolge ausgetriggers.

Nachdem ich die Antworten auf die Frage gelesen habe, denke ich, dass das Einkapseln oder Umbrechen der parseInt-Methode nicht notwendig ist, vielleicht sogar keine gute Idee.

Sie könnten “null” zurückgeben, wie Jon vorgeschlagen hat, aber das ersetzt mehr oder weniger ein try / catch-Konstrukt durch einen Null-Check. Es gibt nur einen kleinen Unterschied im Verhalten, wenn Sie die Fehlerbehandlung “vergessen”: Wenn Sie die Ausnahme nicht abfangen, gibt es keine Zuweisung, und die Variable auf der linken Seite behält ihren alten Wert bei. Wenn Sie nicht auf Null testen, werden Sie wahrscheinlich von der JVM (NPE) getroffen.

Gähnens Vorschlag sieht eleganter aus, weil ich nicht gerne Null zurückgebe, um einige Fehler oder Ausnahmezustände zu signalisieren. Jetzt müssen Sie die Referenzgleichheit mit einem vordefinierten Objekt überprüfen, das auf ein Problem hinweist. Aber, wie andere argumentieren, wenn Sie wieder “vergessen”, zu überprüfen, und ein String ist nicht zu vercasting, wird das Programm mit dem Wrapped-Int in Ihrem “ERROR” oder “NULL” -Objekt fortgesetzt.

Nikolays Lösung ist noch objektorientierter und wird auch mit parseXXX-Methoden anderer Wrapper-classn arbeiten. Aber am Ende hat er nur die NumberFormatException durch eine OperationNotSupported-Ausnahme ersetzt – wieder benötigen Sie einen try / catch, um mit nicht-packbaren Eingaben umzugehen.

Also, es ist meine Schlussfolgerung, die plain-Methode nicht einzukapseln. Ich würde nur einkapseln, wenn ich auch eine (anwendungsabhängige) Fehlerbehandlung hinzufügen könnte.

Möglicherweise können Sie etwas wie folgt verwenden:

 public class Test { public interface Option { T get(); T getOrElse(T def); boolean hasValue(); } final static class Some implements Option { private final T value; public Some(T value) { this.value = value; } @Override public T get() { return value; } @Override public T getOrElse(T def) { return value; } @Override public boolean hasValue() { return true; } } final static class None implements Option { @Override public T get() { throw new UnsupportedOperationException(); } @Override public T getOrElse(T def) { return def; } @Override public boolean hasValue() { return false; } } public static Option parseInt(String s) { Option result = new None(); try { Integer value = Integer.parseInt(s); result = new Some(value); } catch (NumberFormatException e) { } return result; } } 

Sie können das gewünschte C ++ – Verhalten auch sehr einfach replizieren

 public static boolean parseInt(String str, int[] byRef) { if(byRef==null) return false; try { byRef[0] = Integer.parseInt(prop); return true; } catch (NumberFormatException ex) { return false; } } 

Sie würden die Methode wie folgt verwenden:

 int[] byRef = new int[1]; boolean result = parseInt("123",byRef); 

Danach ist die Variable wahr, wenn alles in Ordnung ist und byRef[0] den analysierten Wert enthält.

Persönlich würde ich bleiben, um die Ausnahme zu fangen.

Mein Java ist ein wenig eingerostet, aber lass mich sehen, ob ich dich in die richtige Richtung weisen kann:

 public class Converter { public static Integer parseInt(String str) { Integer n = null; try { n = new Integer(Integer.tryParse(str)); } catch (NumberFormatException ex) { // leave n null, the string is invalid } return n; } } 

Wenn Ihr Rückgabewert null , haben Sie einen ungültigen Wert. Andernfalls haben Sie eine gültige Integer .

Was ist mit Forking der ParseInt- Methode?

Es ist einfach, kopieren Sie einfach den Inhalt in ein neues Dienstprogramm, das Integer oder Optional zurückgibt, und ersetzen Sie die Würfe durch returns. Es scheint, dass es keine Ausnahmen im zugrunde liegenden Code gibt, aber überprüfen Sie besser .

Durch das Überspringen der gesamten Ausnahmebehandlung können Sie bei ungültigen Eingaben etwas Zeit sparen. Und die Methode ist seit JDK 1.0 da, daher ist es unwahrscheinlich, dass Sie viel tun müssen, um es auf dem neuesten Stand zu halten.

Ich würde vorschlagen, dass Sie eine Methode wie betrachten

  IntegerUtilities.isValidInteger(String s) 

die Sie dann implementieren, wie Sie es für richtig halten. Wenn Sie das Ergebnis zurücktragen möchten – vielleicht, weil Sie Integer.parseInt () trotzdem verwenden – können Sie den Array-Trick verwenden.

  IntegerUtilities.isValidInteger(String s, int[] result) 

Hier setzen Sie das Ergebnis [0] auf den ganzzahligen Wert, der im process gefunden wurde.

Dies ist etwas ähnlich wie Nikolay’s Lösung:

  private static class Box { T me; public Box() {} public T get() { return me; } public void set(T fromParse) { me = fromParse; } } private interface Parser { public void setExclusion(String regex); public boolean isExcluded(String s); public T parse(String s); } public static  boolean parser(Box ref, Parser p, String toParse) { if (!p.isExcluded(toParse)) { ref.set(p.parse(toParse)); return true; } else return false; } public static void main(String args[]) { Box a = new Box(); Parser intParser = new Parser() { String myExclusion; public void setExclusion(String regex) { myExclusion = regex; } public boolean isExcluded(String s) { return s.matches(myExclusion); } public Integer parse(String s) { return new Integer(s); } }; intParser.setExclusion("\\D+"); if (parser(a,intParser,"123")) System.out.println(a.get()); if (!parser(a,intParser,"abc")) System.out.println("didn't parse "+a.get()); } 

Die Hauptmethode demos den Code. Eine andere Möglichkeit, die Parser-Schnittstelle zu implementieren, wäre offensichtlich, einfach “\ D +” aus der Konstruktion zu setzen, und die Methoden würden nichts tun.

Sie können Ihre eigenen Rollen StringUtils.isNumeric() , aber es ist genauso einfach, die StringUtils.isNumeric() -Methode von commons lang zu verwenden . Es verwendet Character.isDigit () , um über jedes Zeichen in der Zeichenfolge zu iterieren.

Die Art und Weise, wie ich dieses Problem behandle, ist rekursiv. Zum Beispiel beim Lesen von Daten von der Konsole:

 Java.util.Scanner keyboard = new Java.util.Scanner(System.in); public int GetMyInt(){ int ret; System.out.print("Give me an Int: "); try{ ret = Integer.parseInt(keyboard.NextLine()); } catch(Exception e){ System.out.println("\nThere was an error try again.\n"); ret = GetMyInt(); } return ret; } 

Um eine Ausnahme zu vermeiden, können Sie die Format.parseObject Methode von Java verwenden. Der folgende Code ist im Grunde eine vereinfachte Version der IntegerValidator- class von Apache Common.

 public static boolean tryParse(String s, int[] result) { NumberFormat format = NumberFormat.getIntegerInstance(); ParsePosition position = new ParsePosition(0); Object parsedValue = format.parseObject(s, position); if (position.getErrorIndex() > -1) { return false; } if (position.getIndex() < s.length()) { return false; } result[0] = ((Long) parsedValue).intValue(); return true; } 

Sie können entweder AtomicInteger oder den Array-Trick int[] , je nach Vorliebe.

Hier ist mein Test, der es verwendet -

 int[] i = new int[1]; Assert.assertTrue(IntUtils.tryParse("123", i)); Assert.assertEquals(123, i[0]); 

Ich hatte auch das gleiche Problem. Dies ist eine Methode, die ich geschrieben habe, um den Benutzer nach einer Eingabe zu fragen und die Eingabe nicht zu akzeptieren, außer es ist eine ganze Zahl. Bitte beachten Sie, dass ich ein Anfänger bin, also wenn der Code nicht wie erwartet funktioniert, beschuldige meine Unerfahrenheit!

 private int numberValue(String value, boolean val) throws IOException { //prints the value passed by the code implementer System.out.println(value); //returns 0 is val is passed as false Object num = 0; while (val) { num = br.readLine(); try { Integer numVal = Integer.parseInt((String) num); if (numVal instanceof Integer) { val = false; num = numVal; } } catch (Exception e) { System.out.println("Error. Please input a valid number :-"); } } return ((Integer) num).intValue(); } 

Die Antwort von Jon Skeet ist in Ordnung, aber ich mag es nicht, ein null Integer-Objekt zurückzugeben. Ich finde das verwirrend zu benutzen. Seit Java 8 gibt es (meiner Meinung nach) eine bessere Option, die OptionalInt :

 public static OptionalInt tryParse(String value) { try { return OptionalInt.of(Integer.parseInt(value)); } catch (NumberFormatException e) { return OptionalInt.empty(); } } 

Dies macht es explizit, dass Sie den Fall behandeln müssen, in dem kein Wert verfügbar ist. Ich würde es vorziehen, wenn diese Art von function der Java-Bibliothek in der Zukunft hinzugefügt würde, aber ich weiß nicht, ob das jemals passieren wird.

Dies ist eine Antwort auf die Frage 8391979, “Hat Java eine int.tryparse, die keine Ausnahme für errorshafte Daten austriggers? [Duplicate]”, die geschlossen und mit dieser Frage verknüpft ist.

Edit 2016 08 17: ltrimZeroes-Methoden hinzugefügt und in tryParse () aufgerufen. Ohne führende Nullen in numberString kann es zu falschen Ergebnissen kommen (siehe Kommentare im Code). Es gibt jetzt auch eine öffentliche statische String-Methode “ltrimZeroes (String numberString)”, die für positive und negative “Zahlen” arbeitet (END Edit)

Im Folgenden finden Sie eine rudimentäre Wrapper (boxing) -class für int mit einer hochgeschwindigkeitsoptimierten tryParse () -Methode (ähnlich wie in C #), die den String selbst analysiert und ein wenig schneller ist als Integer.parseInt (String s) aus Java:

 public class IntBoxSimple { // IntBoxSimple - Rudimentary class to implement a C#-like tryParse() method for int // A full blown IntBox class implementation can be found in my Github project // Copyright (c) 2016, Peter Sulzer, Fürth // Program is published under the GNU General Public License (GPL) Version 1 or newer protected int _n; // this "boxes" the int value // BEGIN The following statements are only executed at the // first instantiation of an IntBox (ie only once) or // already compiled into the code at compile time: public static final int MAX_INT_LEN = String.valueOf(Integer.MAX_VALUE).length(); public static final int MIN_INT_LEN = String.valueOf(Integer.MIN_VALUE).length(); public static final int MAX_INT_LASTDEC = Integer.parseInt(String.valueOf(Integer.MAX_VALUE).substring(1)); public static final int MAX_INT_FIRSTDIGIT = Integer.parseInt(String.valueOf(Integer.MAX_VALUE).substring(0, 1)); public static final int MIN_INT_LASTDEC = -Integer.parseInt(String.valueOf(Integer.MIN_VALUE).substring(2)); public static final int MIN_INT_FIRSTDIGIT = Integer.parseInt(String.valueOf(Integer.MIN_VALUE).substring(1,2)); // END The following statements... // ltrimZeroes() methods added 2016 08 16 (are required by tryParse() methods) public static String ltrimZeroes(String s) { if (s.charAt(0) == '-') return ltrimZeroesNegative(s); else return ltrimZeroesPositive(s); } protected static String ltrimZeroesNegative(String s) { int i=1; for ( ; s.charAt(i) == '0'; i++); return ("-"+s.substring(i)); } protected static String ltrimZeroesPositive(String s) { int i=0; for ( ; s.charAt(i) == '0'; i++); return (s.substring(i)); } public static boolean tryParse(String s,IntBoxSimple intBox) { if (intBox == null) // intBoxSimple=new IntBoxSimple(); // This doesn't work, as // intBoxSimple itself is passed by value and cannot changed // for the caller. I. e. "out"-arguments of C# cannot be simulated in Java. return false; // so we simply return false s=s.trim(); // leading and trailing whitespace is allowed for String s int len=s.length(); int rslt=0, d, dfirst=0, i, j; char c=s.charAt(0); if (c == '-') { if (len > MIN_INT_LEN) { // corrected (added) 2016 08 17 s = ltrimZeroesNegative(s); len = s.length(); } if (len >= MIN_INT_LEN) { c = s.charAt(1); if (!Character.isDigit(c)) return false; dfirst = c-'0'; if (len > MIN_INT_LEN || dfirst > MIN_INT_FIRSTDIGIT) return false; } for (i = len - 1, j = 1; i >= 2; --i, j *= 10) { c = s.charAt(i); if (!Character.isDigit(c)) return false; rslt -= (c-'0')*j; } if (len < MIN_INT_LEN) { c = s.charAt(i); if (!Character.isDigit(c)) return false; rslt -= (c-'0')*j; } else { if (dfirst >= MIN_INT_FIRSTDIGIT && rslt < MIN_INT_LASTDEC) return false; rslt -= dfirst * j; } } else { if (len > MAX_INT_LEN) { // corrected (added) 2016 08 16 s = ltrimZeroesPositive(s); len=s.length(); } if (len >= MAX_INT_LEN) { c = s.charAt(0); if (!Character.isDigit(c)) return false; dfirst = c-'0'; if (len > MAX_INT_LEN || dfirst > MAX_INT_FIRSTDIGIT) return false; } for (i = len - 1, j = 1; i >= 1; --i, j *= 10) { c = s.charAt(i); if (!Character.isDigit(c)) return false; rslt += (c-'0')*j; } if (len < MAX_INT_LEN) { c = s.charAt(i); if (!Character.isDigit(c)) return false; rslt += (c-'0')*j; } if (dfirst >= MAX_INT_FIRSTDIGIT && rslt > MAX_INT_LASTDEC) return false; rslt += dfirst*j; } intBox._n=rslt; return true; } // Get the value stored in an IntBoxSimple: public int get_n() { return _n; } public int v() { // alternative shorter version, v for "value" return _n; } // Make objects of IntBoxSimple (needed as constructors are not public): public static IntBoxSimple makeIntBoxSimple() { return new IntBoxSimple(); } public static IntBoxSimple makeIntBoxSimple(int integerNumber) { return new IntBoxSimple(integerNumber); } // constructors are not public(!=: protected IntBoxSimple() {} { _n=0; // default value an IntBoxSimple holds } protected IntBoxSimple(int integerNumber) { _n=integerNumber; } } 

Test / Beispielprogramm für die class IntBoxSimple:

 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class IntBoxSimpleTest { public static void main (String args[]) { IntBoxSimple ibs = IntBoxSimple.makeIntBoxSimple(); String in = null; BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); do { System.out.printf( "Enter an integer number in the range %d to %d:%n", Integer.MIN_VALUE, Integer.MAX_VALUE); try { in = br.readLine(); } catch (IOException ex) {} } while(! IntBoxSimple.tryParse(in, ibs)); System.out.printf("The number you have entered was: %d%n", ibs.v()); } } 

Versuchen Sie es mit dem Argument für reguläre Ausdrücke und Standardparameter

 public static int parseIntWithDefault(String str, int defaultInt) { return str.matches("-?\\d+") ? Integer.parseInt(str) : defaultInt; } int testId = parseIntWithDefault("1001", 0); System.out.print(testId); // 1001 int testId = parseIntWithDefault("test1001", 0); System.out.print(testId); // 1001 int testId = parseIntWithDefault("-1001", 0); System.out.print(testId); // -1001 int testId = parseIntWithDefault("test", 0); System.out.print(testId); // 0 

Wenn Sie apache.commons.lang3 verwenden, verwenden Sie NumberUtils :

 int testId = NumberUtils.toInt("test", 0); System.out.print(testId); // 0 

Ich möchte einen anderen Vorschlag eincasting, der funktioniert, wenn man Ganzzahlen spezifisch anfordert: Verwenden Sie einfach long und verwenden Sie Long.MIN_VALUE für Fehlerfälle. Dies ähnelt dem Ansatz, der für Zeichen in Reader verwendet wird, wobei Reader.read () eine Ganzzahl im Bereich eines Zeichens oder -1 zurückgibt, wenn der Reader leer ist.

Für Float und Double kann NaN in ähnlicher Weise verwendet werden.

 public static long parseInteger(String s) { try { return Integer.parseInt(s); } catch (NumberFormatException e) { return Long.MIN_VALUE; } } // ... long l = parseInteger("ABC"); if (l == Long.MIN_VALUE) { // ... error } else { int i = (int) l; } 

Sie sollten Ausnahmen nicht verwenden, um Ihre Werte zu überprüfen .

Für ein einzelnes Zeichen gibt es eine einfache Lösung:

 Character.isDigit() 

Bei längeren Werten ist es besser, einige Utils zu verwenden. Von Apache bereitgestellte NumberUtils funktionieren hier perfekt:

 NumberUtils.isNumber() 

Bitte überprüfen Sie https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/math/NumberUtils.html

Sie können ein Null-Objekt wie folgt verwenden:

 public class Convert { @SuppressWarnings({"UnnecessaryBoxing"}) public static final Integer NULL = new Integer(0); public static Integer convert(String integer) { try { return Integer.valueOf(integer); } catch (NumberFormatException e) { return NULL; } } public static void main(String[] args) { Integer a = convert("123"); System.out.println("a.equals(123) = " + a.equals(123)); System.out.println("a == NULL " + (a == NULL)); Integer b = convert("onetwothree"); System.out.println("b.equals(123) = " + b.equals(123)); System.out.println("b == NULL " + (b == NULL)); Integer c = convert("0"); System.out.println("equals(0) = " + c.equals(0)); System.out.println("c == NULL " + (c == NULL)); } } 

Das Ergebnis von main in diesem Beispiel ist:

 a.equals(123) = true a == NULL false b.equals(123) = false b == NULL true c.equals(0) = true c == NULL false 

Auf diese Weise können Sie immer auf eine fehlgeschlagene Konvertierung testen, aber trotzdem mit den Ergebnissen als Integer-Instanzen arbeiten. Sie können auch die Zahl NULL (≠ 0) optimieren.