BigDecimal causes NumberFormatException

11,639

You instantiate a Profit in only one place in the code you presented:

Profit profit = new Profit("regular_profit", profitEditText.getText().toString(),
        profitPeriod, picker);

If that causes a NumberFormatException then it must come from this code in the Profit constructor:

if(value!=null){this.value = new BigDecimal(value);}else{ this.value = new BigDecimal("0.0");};

Since the value String will have come from invoking toString() on an object, you can rely on it to not be null (and anyway, if it were null then you would end up at new BigDecimal("0.0"), and you can verify for yourself that that works fine). That leaves us with new BigDecimal(value) as the culprit. The docs for this constructor provide details of the expected format of the argument string. If the argument does not comply with that format then a NumberFormatException will be thrown.

Among the more likely malformed strings that one might imagine in a scenario such as you describe is an empty string, which is not the same as null in Java. Given that the text is coming from a UI field, you also have to watch out for fun and difficult-to-spot malformations such as leading and trailing whitespace.

Perhaps it would be better to write the affected part of the constructior like so:

if (value != null) {
    value = value.trim();
    this.value = value.length() == 0 ? BigDecimal.ZERO : new BigDecimal(value);
} else {
    this.value = BigDecimal.ZERO;
}

Do note that even with that change, this constructor will still throw NumberFormatException in the event of other kinds of malformation. Wherever you are uncertain about the validity of the data, you should be prepared to handle such an exception.

Share:
11,639
Angel
Author by

Angel

Updated on June 04, 2022

Comments

  • Angel
    Angel almost 2 years

    I am working on a financial app and i have to use BigDecimal to calculate currency.Now I am trying to make one entrance activity which is searching in the SharedPreferences for a previous registration and if their is one it starts another activity , if not it starts the registration activity.The problem is the registration activity causes NumberFormatException Error while I am trying to construct a new object of time Profit (the Profit class contains BigDecimal field).Also I am not quite sure that I am using SharedPreferences right.I am not quite sure that my idea and my coding are fine because I am new at android development and i am stuck with this problem so I am giving you all the classes.

    the Registration activity:

    public class Registration extends Activity {
    
    private static String enteredPassword;
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
    
        final EditText password1 = (EditText) findViewById(R.id.password1);
        final EditText password2 = (EditText) findViewById(R.id.password2);
        final EditText availability = (EditText) findViewById(R.id.availability);
        EditText profitEditText = (EditText) findViewById(R.id.profit);
        Spinner spinner = (Spinner) findViewById(R.id.period_spinner);
        Button registrationButton = (Button) findViewById(R.id.registrationButton);
    
        DatePicker picker = (DatePicker) findViewById(R.id.thePicker);
    
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
                R.array.periods_array, android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);
    
    
        final String balance =  availability.getText().toString().replace(",",".");
        String profitPeriod = ((Period) spinner.getSelectedItem()).name();
        final Profit profit = new Profit("regular_profit", profitEditText.getText().toString(),
                profitPeriod ,picker);
    
        registrationButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (password1.getText().toString().equals(password2.getText().toString())) {
                    enteredPassword = password1.getText().toString();
                    Repository.setPassword(enteredPassword, mContext);
                }
                Repository.setBalance(balance, mContext);
                Repository.setProfit(profit, mContext);
            }
        });
    
    }
    

    }

    the profit activity

    public class Profit {
    
    
    
    private Date mProfitDate;
    private Date mNextProfitDate;
    private BigDecimal value;
    
    public final static String TITLE = "title";
    public final static String PERIOD = "period";
    
    
    
    
    Profit(String title,String value, String period, DatePicker picker) {
        this.mTitle = title;
        if(value!=null){this.value = new BigDecimal(value);}else{ this.value = new BigDecimal("0.0");};
        this.mPeriod = Period.class.cast(period);
        this.mProfitDate = Repository.getDateFromPicker(picker);
    }
    
    
    
    
    public long getNextProfitDate(Date profitDate, Period period){
        long unixProfitDate = profitDate.getTime() / 1000L;
        long nextProfitDate=0;
        switch (period){
            case DAILY:
                nextProfitDate = DateUtils.DAY_IN_MILLIS/1000 + unixProfitDate;
                break;
            case WEEKLY:
                nextProfitDate = DateUtils.WEEK_IN_MILLIS/1000 + unixProfitDate;
                break;
            case MONTHLY:
                Date dateNow = new Date();
                dateNow.getTime();
                nextProfitDate = Repository.monthsToSeconds(Calendar.getInstance()
                        .get(Calendar.MONTH)) + unixProfitDate;
                break;
            case YEARLY:
                nextProfitDate = DateUtils.YEAR_IN_MILLIS/1000 + unixProfitDate;
                break;
        }
        return nextProfitDate;
    }
    

    }

    the entrance activity

    public class EnterActivity extends Activity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        setContentView(R.layout.activity_enter);
    
        CountDownTimer timer = new CountDownTimer(3000, 100) {
            public void onTick(long millisUntilFinished) {
            }
             public void onFinish() {
                if(Repository.getPassword(mContext.getApplicationContext()).equals("No Value")){
                    Intent registrationIntent = new Intent(mContext, Registration.class);
                    startActivity(registrationIntent);
                }
                else{
                    Intent configurationIntent = new Intent(mContext, Configuration.class);
                    startActivity(configurationIntent);
                }
            }
        }.start();
    
    }
    

    }

    the Repository

    public class Repository {
    
    public static SharedPreferences sharedPreferences;
    
    
    private static SharedPreferences getPrefs(Context context) {
        return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
    }
    
    public static String getPassword(Context context){
        return getPrefs(context).getString(PASSWORD,"No Value");
    }
    public Repository() {
    }
    
    
    public static void setPassword(String password, Context context){
        SharedPreferences.Editor editor = getPrefs(context).edit();
        editor.putString(PASSWORD, password);
        editor.commit();
    }
    public static void setBalance(String balance, Context context){
        SharedPreferences.Editor editor = getPrefs(context).edit();
        editor.putString(BALANCE, balance);
        editor.commit();
    }
    
    public static void setProfit(Profit profit, Context context){
        SharedPreferences.Editor editor = getPrefs(context).edit();
        editor.putString(BALANCE, profit.getValue().toString());
        editor.commit();
    }
    

    }

    the output

    E/AndroidRuntime: FATAL EXCEPTION: main
            E/AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{/.Registration}: java.lang.NumberFormatException: Bad offset/length: offset=0 len=0 in.length=0
            E/AndroidRuntime:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1956)
            E/AndroidRuntime:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981)
            E/AndroidRuntime:     at android.app.ActivityThread.access$600(ActivityThread.java:123)
            E/AndroidRuntime:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147)
            E/AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:99)
            E/AndroidRuntime:     at android.os.Looper.loop(Looper.java:137)
            E/AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:4424)
            E/AndroidRuntime:     at java.lang.reflect.Method.invokeNative(Native Method)
            E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Method.java:511)
            E/AndroidRuntime:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
            E/AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
            E/AndroidRuntime:     at dalvik.system.NativeStart.main(Native Method)
            E/AndroidRuntime:  Caused by: java.lang.NumberFormatException: Bad offset/length: offset=0 len=0 in.length=0
            E/AndroidRuntime:     at java.math.BigDecimal.<init>(BigDecimal.java:282)
            E/AndroidRuntime:     at java.math.BigDecimal.<init>(BigDecimal.java:438)
            E/AndroidRuntime:     at .Profit.<init>(Profit.java:41)
            E/AndroidRuntime:     at .Registration.onCreate(Registration.java:57)
            E/AndroidRuntime:     at android.app.Activity.performCreate(Activity.java:4466)
            E/AndroidRuntime:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
            E/AndroidRuntime:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1920)
            E/AndroidRuntime:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981)
            E/AndroidRuntime:     at android.app.ActivityThread.access$600(ActivityThread.java:123)
            E/AndroidRuntime:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147)
            E/AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:99)
            E/AndroidRuntime:     at android.os.Looper.loop(Looper.java:137)
            E/AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:4424)
            E/AndroidRuntime:     at java.lang.reflect.Method.invokeNative(Native Method)
            E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Method.java:511)
            E/AndroidRuntime:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
            E/AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
            E/AndroidRuntime:     at dalvik.system.NativeStart.main(Native Method)