1

I have an anonymous class and I want to access some variables of the outer class from the anonymous.The problem is that always when I try to access it, it has the same value as when it was initialized even that i am changing its value on an other anonymous class.

this is the trimmed code

public class WeaponCircle extends Entity {
    public static int TAG = gWorld.getNextTag();
    float power=99; // <<<--the variable
    public WeaponCircle(final gWorld world) {
        super(world);
        this.tag = TAG;

        setGroups(Scene.SLOWABLE);

        addMechanism(new IntervalMechanism(this,10) {
            @Override
            public void init() {
                super.init();
                    world.worldrenderer.setWorldShader("punchShockWave");
                    world.worldrenderer.getShader("punchShockWave").getShader().setUniformf("screenSize",  Tools.tempVec2.set(Gdx.graphics.getWidth(),Gdx.graphics.getHeight()));
                    power=4;
            }
            @Override
            public void die() {
                super.die();
                world.worldrenderer.setClearWorldShader();
            }
            @Override
            public void tick() {//this is called every frame
                if(power>0)power-=0.1f*Director.delta;
                Tools.con("debug0:"+power);    //<<--------------------debug0 ,prints correct value
            }
        });

        addMechanism(new MovementMechanism(this));

        addMechanism(new SpriteMechanism(this,"sprites/sprites.png",714,284,1,235,235,gWorld.RENDER_LAYER4));

        world.worldrenderer.addShader("punchShockWave", new Shader("shaders/default.ver","shaders/punch_shock_wave.frag",Shader.SCENE_SHADER));
        world.worldrenderer.getShader("punchShockWave").setListener(new ShaderParrametersListener(this) {
            @Override
            public void setParameters(Shader shader) {//called every frame
                WeaponCircle wc=(WeaponCircle)entity;//entity is the object that i passed in the constractor
                /*irrelevant code*/
                Tools.con("debug1:"+wc.power);  //<<--------------------debug1 ,prints always 99
                Tools.con("debug2:"+power);   //<<--------------------debug2 ,prints always 99
                /*irrelevant code*/
            }
        });
    }
}

ShaderParrametersListener (its a static class inside the Shader class)

public abstract static class ShaderParrametersListener{
    public Entity entity;
    abstract public void setParameters(Shader shader);
    public ShaderParrametersListener(){};
    public ShaderParrametersListener(Entity entity){this.entity=entity;}
}

My entity system is based on anonymus classes and I didnt had any problem so far ,If i use 1 more IntervlMechanism and print the power variable from there ,I will get the correct value.This happens only in the ShaderParrametersListener class.

SteveL
  • 3,331
  • 4
  • 32
  • 57
  • 5
    `if(power>0)power-=0.1f*Director.delta;` - I'd urge you to consider the readability benefits of whitespace (and indeed braces). – Jon Skeet Jul 05 '14 at 08:08
  • 1
    Does `IntervalMechanism` declare a `power` field? – Henry Jul 05 '14 at 08:11
  • @Henry ,no it doesn't ,all the power appearances refer to the one inside WeaponCircle class, triple checked – SteveL Jul 05 '14 at 08:14
  • What is the order that debug0, debug1, and debug2 are printed in? – newacct Jul 05 '14 at 22:52
  • @newacct ,the order is debug0,debug1,debug2 .Both tick and setParameters() are executing on the same event but setParameters() is the last called.Also everything runs on one thread. – SteveL Jul 06 '14 at 06:56
  • Implement ShaderParrametersListener a power field? – schlagi123 Jul 07 '14 at 12:42
  • @schlagi123 ,No ShaderParrametersListener doesnt have a power field,if i change the power value from 99 to say 76(in the field initialization) I will be getting 76 ,so power inside the ShaderPa.. class is referring to the one in WeaponCircle but its not changing its value. – SteveL Jul 07 '14 at 13:07
  • Can we see the constructor code for ShaderParrametersListener? – Cameron Jul 07 '14 at 15:31
  • @Cameron ,added the code for ShaderParrametersListener. – SteveL Jul 07 '14 at 17:07
  • Are you able to step through/debug the code? Breakpointing on the prints would eliminate threading issues and adding a breakpoint on variable modification would eliminate something resetting the field. – Ordous Jul 07 '14 at 17:25
  • What is the class hierarchy above `IntervalMechanism`? – Stuart Marks Jul 07 '14 at 17:34
  • @StuartMarks , Object->WorldMechanism->IntervalMechanism. None of the hierarchy objects have a power field. – SteveL Jul 07 '14 at 17:41
  • @Ordous ,yes i am able to debug ,but I dont really get any info from it,I put 2 break points ,one in the power-=0.1f... and one in the Tools.con("debug1") . In the first the power increments as it should and in the second the power is just 99 ,it doesn't make sense. – SteveL Jul 07 '14 at 17:45
  • @SteveL What I meant was more like [this](http://stackoverflow.com/questions/4086039/data-breakpoints-in-java-eclipse) – Ordous Jul 07 '14 at 17:50

1 Answers1

1

I think your issue is that you are using the this keyword inside the constructor. See Java - Leaking this in constructor.

As the linked answer says,

"An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields". No such guarantee if you leak this to another thread in the constructor! Also 'at the end of the constructor' may be reordered by the VM."

My guess is that world.worldrenderer.getShader("punchShockWave").setListener is the other thread that now has access to an incompletely constructed object.

I'd recommend finding a way to call the above setListener method after your constructor has completed.

UPDATE I see that the power field is not final, so that may invalidate my hypothesis, as the linked question seems to indicate this problem holds for final fields only; however, I still would attempt to wait until after the constructor completes before passing this to another object. Maybe try it out and see if it fixes the bug.

Community
  • 1
  • 1
Jake Toronto
  • 3,524
  • 2
  • 23
  • 27
  • wow ,I moved the setListener on the tick method(used a boolean 'first' to only call it once) so that it will be called after the constraction of the object has finished and the problem disappears, this is a very good answer ,good work, you will get the bounty in 13 hours... – SteveL Jul 07 '14 at 18:39
  • I'm glad to hear it, SteveL. – Jake Toronto Jul 07 '14 at 18:41
  • I think I was not getting this issue on windows(now running linux) didn't mentioned it cause I was not sure, is this possible? possible that the windows implementation of the vm was doing something different with the threads management?Also I am not getting this problem (as mentioned in the question) in the case where I would access power from another IntervalMechanism anonymous class(or any other that extends WorldMechanism).Is it possible that in some implementations of the virtual machine I will get problems like the above even in this classes? – SteveL Jul 07 '14 at 18:48
  • With my limited understanding of what's happening, I will still say "Yes, it is possible that you will still get problems in the other classes that are currently working." Concurrency is a difficult and tricky thing, and anything you can do to remove any uncertainty or possible errors is a GOOD THING. – Jake Toronto Jul 07 '14 at 18:51