views:

315

answers:

8

Hey, I have a real weird problem. I have 2 classes: (very important note: this is an example cause I can't paste the original code, I wrote it as text with no compiler)

class B {
    private int num = 9;

    public int getNum(){
       return num;
    }

    public void setNum(int num){
       this.num = num;
    }
}

class A {
    private B b = new B();

    public void setB(B b){
        b.setNum(b != null? b.getNum() : 8);
   }

   public B getB(){
       if (b == null)
           System.out.println("How possible?");
       return b;
   }
}

Now, sometimes I get the print... I don't see how's that possible.

A is a serialized class, but yet I can't figure it out...

Thanks, Udi

+5  A: 

It's not possible, no. You'll get a type error in the definition of A.getB() when you try to compile it, and your definition of A.setB() looks dubious as well (shadows b).

Dave Hinton
Not to mention the type error in A.setB().
starblue
Indeed, should be `this.b.setNum(b != null? b.getNum() : 8);` - I've almost added this as an answer, you really should state that more clearly.
Kobi
Again this is an example code. Not the real code.
Udi
@Udi - you expect everyone to find your right errors, but ignore some errors. That's almost impossible. You don't know what your problem is - how can you be sure it's present in your sample code?
Kobi
Im not expecting anyone to find anything in this code. The code is ment to make the question easier to undestand. I added the serialization issue. This is theoretical question
Udi
@udi: but you havent asked anything precise - what is the problem you are facing? how are you running/using this class? give more context for more answers.
Chii
A: 

This line won't compile either:

b.setNum(b != null? b.getNum : new B());
Amarghosh
A: 

sorry, I just can't paste my code. I fixed the getB return type. Anyway, my application compiles and runs.. The question is theoretical

Udi
Ok. In the original code, is b a transient field? I ask because you said the object is serialized.
Bruno Rothgiesser
No. It's not transient..
Udi
Are you asking the question because your boss is insisting on the check for null and you want to prove it's not necessary?
finnw
:) Nope.. Im asking cause cause I have null, and I don't see how it can happen...
Udi
A: 

Well, what about this? If you give B as a parameter, why not use it?

class A {
    private B b = new B();

    public void setB(B b){
        if(b != null) {
            this.b = b;
        }
   }

   public B getB(){
       return b;
   }
}
cause I dont want b to ever be null..
Udi
It is never null! You assigned new B();
a.setB(null) will cause b to be null in your code
Udi
Who would do that? If you want to be sure, replace "this.b = b;" with "if(b != null) this.b = b;"
@rebugger: Please put that into your answer.
Aaron Digulla
@Aaron ah, right. I'm new, but I'm learning ;)
Why does it matter who would do that?! It is my class' logic, and beside I already mentioned that I can't paste my original code so that is a sample code (My original code makes a bit more sense)
Udi
+2  A: 

If you manage to serialize an instance of A which has b == null, then you get a NPE. The reason is that during de-serialization, the constructor isn't invoked and therefore, private B b = new B(); isn't run, so b stays null.

Aaron Digulla
+1  A: 

Did you serialize the instance of A before adding the initialization of B to the class?
If that is the case you could get an instance of A where b is null because the constructors are not called (initializing a member of the class is part of the implicit constructor).
Then you will need to add an implementation of readObject() to class A there you can check whether or not b is null and initialize it if necessary.

Turismo
Do you really mean `readResolve`? `readObject` would do as `b` is not final.
Tom Hawtin - tackline
You are right, readObject will do - I updated the answer.
Turismo
+3  A: 

There are a few circumstances in which b might be null:

  • reflection. b might be set to null reflectively, which circumvents your setter.
  • custom serialisation. b might be explicitly restored as null. Alternatively, if B is not serialisable, you have it marked as transient to avoid errors and it's not restored.


To verify a simple serialisation workflow, use the following code:

    Object object = "someString";

    ByteArrayOutputStream holder = new ByteArrayOutputStream();
    new ObjectOutputStream(holder).writeObject(object);

    Object readObject = new ObjectInputStream(new ByteArrayInputStream(holder.toByteArray())).readObject();

    System.out.println(readObject);

where the first line is replaced by the actual object you wish to test

Robert Munteanu
are you sure reflection "skips" setters? cause it does not skip default constructor as far as I know..
Udi
Reflection does not invoke any setters. Also, constructor invocation rules are a bit more complicated. The rule (IIRC) is that if you have a class hierarchy which contains both `Serializable` and non-`Serializable` classes, all constructors of non-serializable classes will *not* run.
Robert Munteanu
Ok. So is there any wat for me to verify the problem is caused by reflection? I mean any method to override..? Or reflection changes the fields directly?
Udi
Try the simple serialisation workflow I added to my answer.
Robert Munteanu
A: 

Just an idea: replace

System.out.println("How possible?");

with

new Exception().printStackTrace();

That should make it easier for you to see what happened just before. Otherwise, without more information, the only things that seems a possible cause is the serialization.

penpen
Or `Thread.dumpStack();`.
Tom Hawtin - tackline