Хороший способ инкапсулировать целое число.parseInt()
у меня есть проект, в котором мы часто используем Integer.parseInt() для преобразования строки в int. Когда что-то идет не так (например,String это не число, а буква a, или что угодно) этот метод вызовет исключение. Однако, если мне приходится обрабатывать исключения в моем коде везде, это начинает выглядеть очень некрасиво очень быстро. Я хотел бы поместить это в метод, однако, я понятия не имею, как вернуть чистое значение, чтобы показать, что преобразование пошло не так.
В C++ я мог бы создать метод, который принимал указатель на int и позволял самому методу возвращать true или false. Однако, насколько я знаю, это невозможно в Java. Я также мог бы создать объект, содержащий переменную true/false и преобразованное значение, но это тоже не кажется идеальным. То же самое касается глобального значения, и это может дать мне некоторые проблемы с многопоточностью.
есть ли чистый способ сделать это?
22 ответов:
вы можете вернуть
Integerвместоint, возвративnullпри сбое разбора.жаль, что Java не предоставляет способ сделать это без исключения, брошенного внутри - вы можете скрыть исключение (поймав его и вернув null), но это все равно может быть проблемой производительности, если вы анализируете сотни тысяч бит пользовательских данных.
изменить: код для такого метода:
public static Integer tryParse(String text) { try { return Integer.parseInt(text); } catch (NumberFormatException e) { return null; } }обратите внимание, что я не уверен, что с моей головы, что это будет делать, если
textимеет значение null. Вы должны учитывать, что - если он представляет ошибку (т. е. ваш код вполне может передать недопустимое значение, но никогда не должен передавать null), то выбрасывание исключения уместно; если он не представляет ошибку, то вы, вероятно, должны просто вернуть null, как и для любого другого недопустимого значения.Первоначально этот ответ использовал
new Integer(String)конструктор; теперь он используетInteger.parseIntи операция бокса; таким образом, небольшие значения в конечном итоге будет упакован в кэшIntegerобъекты, что делает его более эффективным в тех ситуациях.
какое поведение вы ожидаете, когда это не номер?
если, например, у вас часто есть значение по умолчанию для использования, когда вход не является числом, то такой метод может быть полезен:
public static int parseWithDefault(String number, int defaultVal) { try { return Integer.parseInt(number); } catch (NumberFormatException e) { return defaultVal; } }аналогичные методы могут быть написаны для различных поведения по умолчанию, когда вход не может быть проанализирован.
в некоторых случаях вы должны обрабатывать ошибки синтаксического анализа как отказоустойчивые ситуации, но в других случаях, таких как конфигурация приложения, я предпочитаю обрабатывать отсутствующие входные данные со значениями по умолчанию с помощью Apache Commons Lang 3 NumberUtils.
int port = NumberUtils.toInt(properties.getProperty("port"), 8080);
чтобы избежать обработки исключений использовать регулярное выражение, чтобы убедиться, что у вас есть все цифры первого:
if(value.matches("\d+") { Integer.parseInt(value); }
здесь
Ints.tryParse()на гуавы. Он не создает исключение для нечисловой строки, однако он создает исключение для нулевой строки.
после прочтения ответов на вопрос я думаю, что инкапсулирование или обертывание метода parseInt не обязательно, может быть, даже не очень хорошая идея.
вы можете вернуть "null", как предложил Джон, но это более или менее заменяет конструкцию try/catch нулевой проверкой. Есть только небольшая разница в поведении, если вы "забываете" обработку ошибок: если вы не поймаете исключение, нет назначения, а левая переменная сохраняет его старое значение. Если вы не проверяете для null, вы, вероятно, попадете под JVM (NPE).
предложение зевка выглядит более элегантным для меня, потому что мне не нравится возвращать null, чтобы сигнализировать о некоторых ошибках или исключительных состояниях. Теперь вы должны проверить ссылочное равенство с предопределенным объектом, что указывает на проблему. Но, как утверждают другие, если вы снова "забыли" проверить, и строка не поддается обработке, программа продолжает с обернутым int внутри вашего объекта "ERROR" или "NULL".
решение Николая еще больше объектно-ориентированный и будет работать с методами parseXXX из других классов-оболочек, а также. Но в конце концов он просто заменил исключение NumberFormatException на исключение OperationNotSupported-снова вам нужен try / catch для обработки непространственных входов.
Итак, это мой вывод, чтобы не инкапсулировать простой метод parseInt. Я бы только инкапсулировал, если бы мог добавить некоторую (зависящую от приложения) обработку ошибок.
может быть можно использовать что-то вроде этого:
public class Test { public interface Option<T> { T get(); T getOrElse(T def); boolean hasValue(); } final static class Some<T> implements Option<T> { 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<T> implements Option<T> { @Override public T get() { throw new UnsupportedOperationException(); } @Override public T getOrElse(T def) { return def; } @Override public boolean hasValue() { return false; } } public static Option<Integer> parseInt(String s) { Option<Integer> result = new None<Integer>(); try { Integer value = Integer.parseInt(s); result = new Some<Integer>(value); } catch (NumberFormatException e) { } return result; } }
вы также можете скопировать поведение C++, которое вы хотите очень просто
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; } }вы бы использовали метод так:
int[] byRef = new int[1]; boolean result = parseInt("123",byRef);после этого переменная
resultэто правда, если все прошло хорошо иbyRef[0]содержит анализируемое значение.лично я бы придерживался ловли исключения.
мой Java немного ржавый, но позвольте мне посмотреть, если я могу указать вам в правильном направлении:
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; } }Если ваше возвращаемое значение
null, у вас плохое значение. В противном случае, у вас есть действительныйInteger.
а как же разветвление parseInt способ?
это просто, Просто скопируйте-вставьте содержимое в новую утилиту, которая возвращает
IntegerилиOptional<Integer>и заменить броски с возвратами. Кажется, нет никаких исключений в базовом коде,но лучше проверить.пропуская весь материал обработки исключений, вы можете сэкономить некоторое время на недопустимых входах. И метод существует с JDK 1.0, поэтому вряд ли вам придется много делать, чтобы сохранить это до-до-даты.
Я бы предложил вам рассмотреть такой метод, как
IntegerUtilities.isValidInteger(String s)который вы затем реализовать, как вы считаете нужным. Если вы хотите, чтобы результат был перенесен - возможно, потому, что вы используете Integer.parseInt() в любом случае - вы можете использовать трюк массива.
IntegerUtilities.isValidInteger(String s, int[] result)где вы устанавливаете result[0] в целочисленное значение, найденное в процессе.
это несколько похоже на решение Николая:
private static class Box<T> { T me; public Box() {} public T get() { return me; } public void set(T fromParse) { me = fromParse; } } private interface Parser<T> { public void setExclusion(String regex); public boolean isExcluded(String s); public T parse(String s); } public static <T> boolean parser(Box<T> ref, Parser<T> p, String toParse) { if (!p.isExcluded(toParse)) { ref.set(p.parse(toParse)); return true; } else return false; } public static void main(String args[]) { Box<Integer> a = new Box<Integer>(); Parser<Integer> intParser = new Parser<Integer>() { 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()); }основной метод демонстрирует код. Другим способом реализации интерфейса парсера, очевидно, было бы просто установить "\D+" из конструкции, и методы ничего не делают.
вы может свернуть свой собственный, но это так же легко использовать Викискладе Ланг
StringUtils.isNumeric()метод. Он использует символ.isDigit() для перебора каждого символа в строке.
они, как я справляюсь с этой проблемой рекурсивно. Например, при чтении данных из консоли:
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; }
чтобы избежать исключения, вы можете использовать Java
Format.parseObjectметод. Приведенный ниже код в основном представляет собой упрощенную версию Apache Common's IntegerValidator класса.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; }можно использовать
AtomicIntegerилиint[]массив-трик в зависимости от ваших предпочтений.вот мой тест, который использует его -
int[] i = new int[1]; Assert.assertTrue(IntUtils.tryParse("123", i)); Assert.assertEquals(123, i[0]);
Я тоже была такая же проблема. Это метод, который я написал, чтобы попросить пользователя о вводе и не принимать ввод, если он не является целым числом. Обратите внимание, что я новичок, так что если код не работает, как ожидалось, виновата моя неопытность !
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(); }
ответ, данный Джоном скитом, прекрасен, но мне не нравится возвращать
nullinteger объект. Я считаю, это сбивает с толку, чтобы использовать. Поскольку Java 8 есть лучший вариант (на мой взгляд), используяOptionalInt:public static OptionalInt tryParse(String value) { try { return OptionalInt.of(Integer.parseInt(value)); } catch (NumberFormatException e) { return OptionalInt.empty(); } }это делает его явным, что вы должны обрабатывать случай, когда значение не доступно. Я бы предпочел, чтобы такая функция была добавлена в библиотеку java в будущем, но я не знаю, произойдет ли это когда-нибудь.
это ответ на вопрос 8391979, "есть ли у java int.tryparse, который не создает исключение для плохих данных? [дубликат]", который закрыт и связан с этим вопросом.
Edit 2016 08 17: добавлены методы ltrimZeroes и вызваны они в tryParse(). Без ведущих нулей в numberString может дать ложные результаты (см. комментарии в коде). Теперь также существует открытый статический метод String ltrimZeroes (String numberString), который работает для положительных и отрицательных "чисел"(конец Редактирование)
ниже вы найдете рудиментарный класс обертки (бокс) для int с оптимизированным методом tryParse () с высокой скоростью (аналогично C#), который анализирует саму строку и немного быстрее, чем Integer.parseInt (String s) из 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 (i. e. 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; } }тест / пример программы для класса 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()); } }
попробуйте с регулярным выражением и аргументом параметров по умолчанию
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Если вы используете apache.палата общин.lang3 затем с помощью NumberUtils:
int testId = NumberUtils.toInt("test", 0); System.out.print(testId); // 0
Я хотел бы добавить еще одно предложение, которое работает, если специально запрашивать целые числа: просто используйте long и используйте Long.MIN_VALUE для случаев ошибок. Это похоже на подход, который используется для символов в Reader where Reader.read() возвращает целое число типа char или -1, если читатель пуст.
для поплавка и двойника, NaN можно использовать в подобном путе.
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; }
вы не должны использовать исключения для проверки значения.
для одного символа есть простое решение:
Character.isDigit()для более длинных значений лучше использовать некоторые utils. NumberUtils, предоставленные Apache, будут отлично работать здесь:
NumberUtils.isNumber()пожалуйста, проверьте https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/math/NumberUtils.html
Вы можете использовать нулевой объект вот так:
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)); } }результат главная в этом примере:
a.equals(123) = true a == NULL false b.equals(123) = false b == NULL true c.equals(0) = true c == NULL falseтаким образом, вы всегда можете проверить неудачное преобразование, но по-прежнему работать с результатами в виде целых экземпляров. Вы также можете настроить число NULL представляет (≠ 0).
Comments