3

I have a Value Class and there is an Object value in it. I want to use the value as String, Integer or Double.

The asDouble method controls instanceof value object. If it is not Double or Integer returns 0.

Generally it works but sometimes although it is Double returns 0;. I couldn't the reason of it.

When I debug I can see:

you can check

Here is my Value class

public class Value{
    public Object value;

    public Value(Object value) {
        this.value = value;
    }

    public Double asDouble() {
        if (this.value instanceof Integer || this.value instanceof Double) {
            return Double.parseDouble(String.valueOf(this.value));
        } else {
            return 0.0; 
        }
    }
}

Could anybody explain where did I go wrong

Zabuzard
  • 25,064
  • 8
  • 58
  • 82
Tekin Güllü
  • 363
  • 1
  • 3
  • 18
  • 5
    Two possibilities come to mind: You've got a class called `Double` somewhere that isn't `java.lang.Double` or you've got multithreaded code modifying `value` while you read it. – Joachim Sauer Apr 16 '18 at 14:57
  • 2
    Can you create a [mcve]? You are missing the input data that leads to this "bug", we need it to reproduce the issue. Otherwise we can only guess what the reason is. Probably you only thought you entered a `Double` to `value`, but did enter a different object. Like a `String` maybe `"0.2"` instead of just `0.2`. – Zabuzard Apr 16 '18 at 15:00
  • 5
    From your image it may seem that you have happened to nest two `Value` objects inside each other, only the inner one containing a `Double`. So `this.value` is a `Value`, not a `Double`, so the `else` part is executed. – Ole V.V. Apr 16 '18 at 15:14
  • 2
    Instead of checking explicitly for the types Integer or Double, you could just check if it is `Number` then call `.doubleValue()` to get the value as a double. – killjoy Apr 16 '18 at 15:18
  • 1
    @OleV.V. agree - a `Value` instance inside another `Value` indeed seems to be what's shown in the hover. – david a. Apr 16 '18 at 15:38
  • 1
    Really, you could make life easier if you wouldn't accept all `Object`s but just the three types you want. Then it can't ever happen that you accidentally add something wrong. And depending on the specific usage it might even be better to use some more specific container class instead. Using `Object` is rarely a good idea, you throw away all compiler-help. – Zabuzard Apr 16 '18 at 15:43
  • Agree with killjoy's comment above. If it's already a double, then get the double value. Converting it into a string and then back into a double will lose precision and lower performance and increase memory usage. – DodgyCodeException Apr 16 '18 at 15:58

3 Answers3

5

Short Answer: Details matter.

Some info: The value class instance variable (also-known-as property or member variable) is a class named Value. Since the class Value is neither the Integer nor the Double class, the else branch will always execute.

The Value class also has a member variable named value, which, in your example, has a type of Double.

In the screen shot you included, the Value object has its value member variable set to a "thing" which also has the type Value. I know this because the dialog shows "value= Value (id:82)".

DwB
  • 37,124
  • 11
  • 56
  • 82
2

first at all, i agree with Ole V.V.s, killjoys and david as comments.

your screenshot shows a Value-Object holding another Value-Object in its value-property

the comments and a possible solution are summed up in the following example-class

public class Value {
    // visibility should be private
    private final Object value;

    // to be more type-save 
    // and to restrict
    // this.values instance-type to Double, Integer and/or String
    // provide three public constructors
    public Value(final Double value) {
        this.value = value;
    }

    public Value(final Integer value) {
        this.value = value;
    }

    public Value(final String value) {
        this.value = value;
    }

    public double asDoublePrimitive() {
        if ((this.value instanceof Number)) {
            return ((Number) this.value).doubleValue();
        }
        return 0.0;
    }

    public Double asDoubleObject() {
        if ((this.value instanceof Number)) {
            return Double.valueOf(((Number) this.value).doubleValue());
        }
        return Double.valueOf(0.0);
    }
}

be aware that neither your nor my code consider the case that this.value is an instanceof String

  • There's an unnecessary object creation in `asDoubleObject()` in the case when `value instanceOf Double`. In that case, I would simply return `(Double) value`. – DodgyCodeException Apr 16 '18 at 19:56
1

instanceof check applies to full class definiton which includes the ClassLoader that loaded the class. Probably this.value was loaded by different ClassLoader and the check fails. You can confirm by trying:

ClassLoader cl1 = this.value.getClass().getClassLoader()
ClassLoader cl2 = Double.class.getClassLoader();
if (cl1.equals(cl2)) {
   ...
}

Although this can get even more trickier with parent-child relationships between ClassLoader objects this answer provides a good example when instanceof fails.

The other options is that your Double is not a java.lang.Double but that would be surprising.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111