Хороший способ инкапсуляции Integer.parseInt()
У меня есть проект, в котором мы часто используем Integer.parseInt()
для преобразования String в int. Когда что-то пойдет не так (например, String
- это не число, а буква a
или что-то еще), этот метод будет генерировать исключение. Однако, если мне приходится обрабатывать исключения в моем коде повсюду, это начинает выглядеть очень уродливо очень быстро. Я хотел бы поместить это в метод, однако я не знаю, как вернуть чистую ценность, чтобы показать, что преобразование пошло не так.
В С++ я мог бы создать метод, который принял указатель на int и пусть сам метод вернет true или false. Однако, насколько я знаю, это невозможно в Java. Я мог бы также создать объект, содержащий переменную true/false и преобразованное значение, но это тоже не кажется идеальным. То же самое происходит и для глобальной ценности, и это может вызвать некоторые проблемы при многопоточности.
Так есть ли чистый способ сделать это?
Ответы
Ответ 1
Вы можете вернуть Integer
вместо int
, возвращая null
при сбое разбора.
Стыдно, что Java не обеспечивает способ сделать это, если не исключено внутреннее исключение - вы можете скрыть исключение (поймав его и вернув null), но это все равно может быть проблемой производительности, повторно разбирая сотни тысяч бит данных, предоставленных пользователем.
EDIT: Код для такого метода:
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
, что сделает его более эффективным в этих ситуациях.
Ответ 2
Какое поведение вы ожидаете, когда это не число?
Если, например, вы часто используете значение по умолчанию для использования, когда ввод не является числом, тогда может быть полезен такой метод:
public static int parseWithDefault(String number, int defaultVal) {
try {
return Integer.parseInt(number);
} catch (NumberFormatException e) {
return defaultVal;
}
}
Аналогичные методы могут быть записаны для разных действий по умолчанию, когда вход не может быть проанализирован.
Ответ 3
В некоторых случаях вы должны обрабатывать ошибки синтаксического анализа как аварийные ситуации, но в других случаях, таких как конфигурация приложения, я предпочитаю обрабатывать отсутствующие данные со значениями по умолчанию, используя Apache Commons Lang 3 NumberUtils.
int port = NumberUtils.toInt(properties.getProperty("port"), 8080);
Ответ 4
Чтобы избежать обработки исключений, используйте регулярное выражение, чтобы убедиться, что у вас есть все цифры:
if(value.matches("\\d+") {
Integer.parseInt(value);
}
Ответ 5
Существует Ints.tryParse()
в гуавы. Он не генерирует исключение для нечисловой строки, однако он генерирует исключение для пустой строки.
Ответ 6
Может быть, вы можете использовать что-то вроде этого:
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;
}
}
Ответ 7
После прочтения ответов на вопрос, я думаю, что инкапсуляция или упаковка метода parseInt не нужна, может быть, даже не очень хорошая идея.
Вы можете вернуть "нуль", как предложил Джон, но более или менее заменяя конструкцию try/catch с помощью null-check. Там только небольшая разница в поведении, если вы "забыли" обработку ошибок: если вы не поймаете исключение, то нет назначения, а переменная с левой стороны сохраняет старое значение. Если вы не проверите значение null, вы, вероятно, попадете в JVM (NPE).
Предложение yawn выглядит более элегантно для меня, потому что мне не нравится возвращать null, чтобы сигнализировать о некоторых ошибках или исключительных состояниях. Теперь вам нужно проверить ссылочное равенство с предопределенным объектом, что указывает на проблему. Но, как утверждают другие, если вы снова забыли "проверить", а String не поддается анализу, программа будет продолжена с завернутым int внутри вашего объекта "ERROR" или "NULL".
Решение Nikolay еще более ориентировано на объект и будет работать с методами parseXXX из других классов-оболочек. Но, в конце концов, он просто заменил NumberFormatException на исключение OperationNotSupported - снова вам понадобится try/catch для обработки невоспроизводимых входов.
Итак, это мой вывод, чтобы не инкапсулировать простой метод parseInt. Я бы только инкапсулировал, если бы я мог добавить некоторую (зависимую от приложения) обработку ошибок.
Ответ 8
Вы также можете очень быстро скопировать поведение С++, которое вы хотите просто
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
имеет значение true, если все прошло хорошо, а byRef[0]
содержит проанализированное значение.
Лично я буду придерживаться исключения.
Ответ 9
Моя 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
.
Ответ 10
Ответ, данный Jon Skeet, прекрасен, но мне не нравится возвращать объект Integer null
. Мне кажется, что это сбивает с толку. С Java 8 есть лучший вариант (на мой взгляд), используя OptionalInt
:
public static OptionalInt tryParse(String value) {
try {
return OptionalInt.of(Integer.parseInt(value));
} catch (NumberFormatException e) {
return OptionalInt.empty();
}
}
Это делает его явным, что вы должны обрабатывать случай, когда значение не доступно. Я бы предпочел, чтобы в будущем эта функция была добавлена в java-библиотеку, но я не знаю, произойдет ли это когда-нибудь.
Ответ 11
Как насчет форматирования метода parseInt?
Просто, просто скопируйте содержимое в новую утилиту, которая возвращает Integer
или Optional<Integer>
, и замените броски на возврат. Кажется, в базовом коде нет исключений, но лучше проверить.
Пропуская всю обработку исключений, вы можете сэкономить некоторое время на недопустимых входах. И метод существует с JDK 1.0, поэтому маловероятно, что вам придется много сделать, чтобы поддерживать его в актуальном состоянии.
Ответ 12
Я бы предложил вам рассмотреть такой метод, как
IntegerUtilities.isValidInteger(String s)
который вы затем реализуете по своему усмотрению. Если вы хотите вернуть результат - возможно, потому, что вы используете Integer.parseInt() в любом случае - вы можете использовать трюк массива.
IntegerUtilities.isValidInteger(String s, int[] result)
где вы устанавливаете результат [0] на целое значение, найденное в процессе.
Ответ 13
Это несколько похоже на решение Николая:
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());
}
Основной метод демонстрирует код. Другой способ реализации интерфейса Parser, очевидно, состоял бы в том, чтобы просто установить "\ D +" из построения, и методы не делают ничего.
Ответ 14
Вы можете опрокинуть свой собственный, но так же просто использовать общедоступный метод StringUtils.isNumeric()
. Он использует Character.isDigit() для повторения каждого символа в строке.
Ответ 15
Как я справляюсь с этой проблемой, рекурсивно. Например, при чтении данных с консоли:
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;
}
Ответ 16
Чтобы избежать исключения, вы можете использовать метод Java Format.parseObject
. Ниже приведен код упрощенной версии класса Apache Common 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]);
Ответ 17
У меня тоже была такая же проблема. Это метод, который я написал, чтобы спросить пользователя о вводе и не принимать входные данные, если это не целое число. Обратите внимание, что я новичок, поэтому, если код не работает должным образом, обвините мою неопытность!
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();
}
Ответ 18
Это ответ на вопрос 8391979: "Есть ли у java int.tryparse, который не генерирует исключение для плохих данных? [duplicate]", который закрыт и связан с этим вопросом.
Edit 2016 08 17: Добавлены методы ltrimZeroes и вызваны в tryParse(). Без начальных нулей в numberString могут появляться ложные результаты (см. Комментарии в коде). В настоящее время существует также открытый статический метод String ltrimZeroes (String numberString), который работает для положительных и отрицательных "чисел" (END Edit)
Ниже вы найдете рудиментарный класс Wrapper (бокс) для int с высокоскоростным оптимизированным методом tryParse() (похожим на С#), который анализирует сама строку и немного быстрее, чем 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());
}
}
Ответ 19
Попробуйте с аргументом параметров регулярного выражения и параметров по умолчанию
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.commons.lang3, а затем NumberUtils:
int testId = NumberUtils.toInt("test", 0);
System.out.print(testId); // 0
Ответ 20
Я хотел бы добавить другое предложение, которое работает, если кто-то специально запрашивает целые числа: просто используйте long и используйте Long.MIN_VALUE для случаев ошибки. Это похоже на подход, который используется для символов в Reader, где Reader.read() возвращает целое число в диапазоне символа или -1, если читатель пуст.
Для Float и Double 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;
}
Ответ 21
Вы не должны использовать исключения для проверки ваших значений.
Для одного символа есть простое решение:
Character.isDigit()
Для более длинных значений лучше использовать некоторые утилиты. NumberUtils, предоставляемые Apache, будут отлично работать здесь:
NumberUtils.isNumber()
Пожалуйста, проверьте https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/math/NumberUtils.html
Ответ 22
Если вы используете Java 8 или выше, вы можете использовать библиотеку, которую я только что выпустил: https://github.com/robtimus/try-parse. Он поддерживает int, long и boolean, который не зависит от перехвата исключений. В отличие от Guava Ints.tryParse, он возвращает OptionalInt/OptionalLong/Optional, очень похожий на fooobar.com/questions/80015/... но более эффективный.
Ответ 23
Вы можете использовать Null-Object следующим образом:
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));
}
}
В этом примере результат main:
a.equals(123) = true
a == NULL false
b.equals(123) = false
b == NULL true
c.equals(0) = true
c == NULL false
Таким образом, вы всегда можете проверить неудачное преобразование, но по-прежнему работать с результатами в виде экземпляров Integer. Вы также можете настроить значение NULL, представляющее (≠ 0).