封装Integer.parseInt()的好方法

时间:2009-09-28 09:02:09

标签: java exception-handling encapsulation

我有一个项目,我们经常使用Integer.parseInt()将String转换为int。当出现问题时(例如,String不是数字而是字母a或其他),此方法将抛出异常。但是,如果我必须在我的代码中处理各种异常,那么这很快就会变得非常丑陋。我想把它放在一个方法中,但是,我不知道如何返回一个干净的值来表明转换出错了。

在C ++中,我可以创建一个接受指向int的指针的方法,让方法本身返回true或false。但是,据我所知,这在Java中是不可能的。我还可以创建一个包含true / false变量和转换值的对象,但这似乎也不理想。对于全局值也是如此,这可能会给我带来一些多线程的麻烦。

那么有一个干净的方法吗?

24 个答案:

答案 0 :(得分:128)

您可以返回Integer而不是int,在解析失败时返回null

遗憾的是,如果没有内部抛出的异常,Java没有提供这样做的方法 - 你可以隐藏异常(通过捕获它并返回null),但如果你'它仍然可能是一个性能问题'重新解析数十万个用户提供的数据。

编辑:这种方法的代码:

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

请注意,如果text为空,我不确定这会做些什么。您应该考虑 - 如果它代表一个错误(即您的代码可能传递无效值,但永远不应传递null),那么抛出异常是合适的;如果它不代表一个bug,那么你应该像对任何其他无效值一样返回null。

最初这个答案使用了new Integer(String)构造函数;它现在使用Integer.parseInt和拳击操作;通过这种方式,小值将最终被装箱到缓存的Integer对象,使其在这些情况下更有效。

答案 1 :(得分:33)

当它不是数字时,您期望什么行为?

例如,如果您在输入不是数字时经常使用默认值,那么这样的方法可能很有用:

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

当无法解析输入时,可以为不同的默认行为编写类似的方法。

答案 2 :(得分:24)

在某些情况下,您应该将解析错误视为快速失败的情况,但在其他情况下,例如应用程序配置,我更喜欢使用Apache Commons Lang 3 NumberUtils处理缺省值的输入。

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

答案 3 :(得分:15)

为避免处理异常,请使用正则表达式确保首先包含所有数字:

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

答案 4 :(得分:10)

Guava中有Ints.tryParse()。它不会在非数字字符串上抛出异常,但它会在空字符串上抛出异常。

答案 5 :(得分:4)

可能你可以使用这样的东西:

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

}

答案 6 :(得分:4)

在阅读问题的答案后,我认为封装或包装parseInt方法不是必需的,甚至可能不是一个好主意。

你可以像Jon建议的那样返回'null',但这或多或少是通过null-check替换try / catch构造。如果你'忘记'错误处理,那么行为只会略有不同:如果你没有捕获异常,那么就没有赋值,左侧变量保持旧值。如果你不测试null,你可能会受到JVM(NPE)的攻击。<​​/ p> 哈欠的建议对我来说看起来更优雅,因为我不喜欢返回null来表示一些错误或异常状态。现在,您必须检查与预定义对象的引用相等性,这表示存在问题。但是,正如其他人所说的那样,如果再次忘记'检查并且String是不可解析的,程序将继续使用'ERROR'或'NULL'对象中的包装int。

Nikolay的解决方案更加面向对象,并且可以与其他包装类的parseXXX方法一起使用。但最后,他只是用OperationNotSupported异常替换了NumberFormatException - 再次需要try / catch来处理不可解决的输入。

所以,我的结论是没有封装普通的parseInt方法。如果我还可以添加一些(应用程序依赖的)错误处理,我只会封装。

答案 7 :(得分:2)

您还可以复制非常简单的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]包含已解析的值,则为真。

就个人而言,我会坚持抓住例外。

答案 8 :(得分:1)

我的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

答案 9 :(得分:1)

如果您使用的是Java 8或更高版本,则可以使用我刚发布的库:https://github.com/robtimus/try-parse。它支持不依赖于捕获异常的int,long和boolean。与Guava的Ints.tryParse不同,它返回OptionalInt / OptionalLong / Optional,与https://stackoverflow.com/a/38451745/1180351中的类似,但效率更高。

答案 10 :(得分:1)

分析parseInt 方法怎么样?

这很简单,只需将内容复制粘贴到返回IntegerOptional<Integer>的新实用程序,并将throws替换为返回值。似乎底层代码中没有例外,但是更好地检查

通过跳过整个异常处理内容,您可以节省一些无效输入的时间。从JDK 1.0开始,这个方法就存在了,所以你不一定要做很多工作来保持它的最新状态。

答案 11 :(得分:1)

Jon Skeet给出的答案很好,但我不喜欢回馈null整数对象。我发现这令人困惑。从Java 8开始,有一个更好的选择(在我看来),使用OptionalInt

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

这明确表示您必须处理没有可用值的情况。我希望将来可以将这种函数添加到java库中,但我不知道是否会发生这种情况。

答案 12 :(得分:0)

为避免异常,您可以使用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;
}

您可以使用AtomicIntegerint[]阵列技巧,具体取决于您的偏好。

以下是我使用它的测试 -

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

答案 13 :(得分:0)

他们以递归的方式处理这个问题。例如,从控制台读取数据时:

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

答案 14 :(得分:0)

可以滚动你自己的,但使用公共语言StringUtils.isNumeric() method同样容易。它使用Character.isDigit()迭代字符串中的每个字符。

答案 15 :(得分: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();
}

答案 16 :(得分:0)

这与Nikolay的解决方案有些相似:

 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 +”,并且方法不执行任何操作。

答案 17 :(得分:0)

我建议你考虑像

这样的方法
 IntegerUtilities.isValidInteger(String s)

然后根据需要实施。如果你想要结果 - 也许是因为你使用了Integer.parseInt() - 你可以使用数组技巧。

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

将result [0]设置为过程中找到的整数值。

答案 18 :(得分:0)

尝试使用正则表达式和默认参数参数

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

答案 19 :(得分:0)

如果有人专门请求整数,我想提出另一个有效的建议:只需使用long并使用Long.MIN_VALUE来处理错误情况。这类似于Reader中用于chars的方法,其中Reader.read()返回char范围内的整数,如果读取器为空,则返回-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;
}

答案 20 :(得分:0)

您不应使用例外来验证您的值

对于单个字符,有一个简单的解决方案:

Character.isDigit()

对于更长的值,最好使用一些utils。 Apache提供的NumberUtils在这里可以很好地工作:

NumberUtils.isNumber()

请检查https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/math/NumberUtils.html

答案 21 :(得分:0)

考虑到现有的答案,我已经复制粘贴并增强了Integer.parseInt的源代码来完成这项工作,并提出了解决方案

  • 不使用可能会很慢的try-catch(不同于Lang 3 NumberUtils),
  • 不使用不能捕获太大数字的正则表达式,
  • 避免拳击(与番石榴的Ints.tryParse()不同)
  • 不需要任何分配(不同于int[]BoxOptionalInt
  • 接受任何CharSequence或其中一部分,而不是整个String
  • 可以使用Integer.parseInt可以使用的任何基数,即[2,36],
  • 不依赖任何库。

唯一的缺点是toIntOfDefault("-1", -1)toIntOrDefault("oops", -1)之间没有区别。

public static int toIntOrDefault(CharSequence s, int def) {
    return toIntOrDefault0(s, 0, s.length(), 10, def);
}
public static int toIntOrDefault(CharSequence s, int def, int radix) {
    radixCheck(radix);
    return toIntOrDefault0(s, 0, s.length(), radix, def);
}
public static int toIntOrDefault(CharSequence s, int start, int endExclusive, int def) {
    boundsCheck(start, endExclusive, s.length());
    return toIntOrDefault0(s, start, endExclusive, 10, def);
}
public static int toIntOrDefault(CharSequence s, int start, int endExclusive, int radix, int def) {
    radixCheck(radix);
    boundsCheck(start, endExclusive, s.length());
    return toIntOrDefault0(s, start, endExclusive, radix, def);
}
private static int toIntOrDefault0(CharSequence s, int start, int endExclusive, int radix, int def) {
    if (start == endExclusive) return def; // empty

    boolean negative = false;
    int limit = -Integer.MAX_VALUE;

    char firstChar = s.charAt(start);
    if (firstChar < '0') { // Possible leading "+" or "-"
        if (firstChar == '-') {
            negative = true;
            limit = Integer.MIN_VALUE;
        } else if (firstChar != '+') {
            return def;
        }

        start++;
        // Cannot have lone "+" or "-"
        if (start == endExclusive) return def;
    }
    int multmin = limit / radix;
    int result = 0;
    while (start < endExclusive) {
        // Accumulating negatively avoids surprises near MAX_VALUE
        int digit = Character.digit(s.charAt(start++), radix);
        if (digit < 0 || result < multmin) return def;
        result *= radix;
        if (result < limit + digit) return def;
        result -= digit;
    }
    return negative ? result : -result;
}
private static void radixCheck(int radix) {
    if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
        throw new NumberFormatException(
                "radix=" + radix + " ∉ [" +  Character.MIN_RADIX + "," + Character.MAX_RADIX + "]");
}
private static void boundsCheck(int start, int endExclusive, int len) {
    if (start < 0 || start > len || start > endExclusive)
        throw new IndexOutOfBoundsException("start=" + start + " ∉ [0, min(" + len + ", " + endExclusive + ")]");
    if (endExclusive > len)
        throw new IndexOutOfBoundsException("endExclusive=" + endExclusive + " > s.length=" + len);
}

答案 22 :(得分:0)

也许有人正在寻找一种更通用的方法,因为Java 8中提供了用于定义供应商功能的软件包java.util.function。您可能具有接受供应商和默认值的函数,如下所示:

public static <T> T tryGetOrDefault(Supplier<T> supplier, T defaultValue) {
    try {
        return supplier.get();
    } catch (Exception e) {
        return defaultValue;
    }
}

使用此功能,您可以执行任何可能引发异常的解析方法,甚至其他方法,同时确保不会抛出异常:

Integer i = tryGetOrDefault(() -> Integer.parseInt(stringValue), 0);
Long l = tryGetOrDefault(() -> Long.parseLong(stringValue), 0l);
Double d = tryGetOrDefault(() -> Double.parseDouble(stringValue), 0d);

答案 23 :(得分:-1)

您可以像这样使用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

这样,您始终可以测试失败的转换,但仍然可以将结果作为整数实例使用。您可能还想调整数字 NULL 表示(≠0)。