0

I am a beginner in java programming and I am currently studying programming for libGDX. In the book "Beginning Java Game Development with libGDX" there are instructions on how to make some basic games. One of them (the game that is an introduction to processing mouse and touch input) we make a game where balloons spawn on the left side of the screen and these balloons keep moving to the right side and each of them is removed from the game either when you click on it, or when it goes off screen (when X is greater than the width of the screen) I have a problem understanding the use of final in this code:

spawnTimer += delta;

// check time for next balloon spawn
if(spawnTimer > spawnInterval)
{
    spawnTimer -= spawnInterval;
    final Balloon b = new Balloon();
    b.addListener
    (
        new InputListener()
        {
            public boolean touchDown (InputEvent event, float x, float y, int pointer, int buttoon)
            {
                popped++;
                b.remove();
                return true;

            }
        }
    );
    mainStage.addActor(b);
}

The code inside of the if statement is responsible for creating the instance of an actor called Balloon and add to it an Input Listener so it will execute this code:

popped++;
b.remove();
return true;

when it is touched. In the book final wasn't used for creating a new instance of Balloon and I only used it because Android Studio made me do it ( it said that I couldn't use b.remove() because it is a code that will be used inside the instance or something like that) and it seems like it works. I want to know why using final works.

Is the final object disposed of when the method (update()) is finished doing its thing (and that's why it won't bug then the line is used again)?

When the b.remove() is executed, how will it know what it is going to remove?

And when I create an instance like that I thought it was giving it a name but it is just creating an instance and a pointer, right? So, if I kind of loose that pointer I just created I can only get another one from the inside of the instance (or maybe from the mainStage)?

Kevin
  • 93
  • 2
  • 12
  • That variable must be `final` because it's used in an inner class. That means that the reference cannot be changed once initialized, not that it lives forever. – Brick Sep 11 '17 at 01:09
  • Yes, creating objects in java just spawns a new instance an updates the pointer. And you can't directly interact with pointers in Java, so you would have to maintain a reference somewhere in your application. Garbage Collection in Java automatically deletes anything that you don't hold a reference to anymore. – SpacePrez Sep 14 '17 at 20:10

1 Answers1

-1

So, when you're calling new InputListener() {... you probably already understand that you're creating an anonymous inner class. One of the major benefits of using an anonymous inner class is that you can declare it inside of a class member (such as a method) and therefore the inner class can have access to elements of the enclosing member, such as the variable Balloon b. You have captured the value that's a variable of an enclosing method. In other languages this is commonly called a 'closure'. if you examine the bytecode that is produced, you will see that your new inner class will have a field of type Balloon and a constructor parameter of type Balloon. Using the constructor, the variable b is copied into the field of your inner class.

Before Java, there have been plenty of programming languages that allowed you capture values in this way, but many of them have caused difficult-to-see problems in code, because those variables can normally be freely changed within a method. The touchDown method of your inner class will probably not be executed immediately after b.addListener gets called. What if code in that enclosing method assigns a different object to b before touchDown is executed? Which reference of b should be used when it's being executed? The original one, or the new one?

The way that Java solves this problem is by insisting that any method variable that is captured by an inner class be final, which ensures that it will not be changed later in the method. If you attempt to capture a variable that is not final, the compiler will fail with an error.

As a side-note, Java 8 style lambda expressions compile down internally to inner classes, but they actually have slightly different rules for capturing variables, but the results are the same. Instead of insisting that any variable captured be explicitly final, the enclosing method is statically analyzed to ensure that no code assigns to that variable. If it does, you will get an error that the variable must be 'effectively' final.

Avi Cherry
  • 3,996
  • 1
  • 26
  • 31
  • Lambdas do not compile to inner classes in many (all?) major compilers. Beyond that, it's definitely true that there is no specification saying that it will happen that way. https://stackoverflow.com/questions/16827262/how-will-java-lambda-functions-be-compiled and http://www.logicbig.com/tutorials/core-java-tutorial/java-8-enhancements/java-lambda-functional-aspect/ – Brick Sep 11 '17 at 01:41
  • Thanks for the information @brick ! Most of my Java work is on Android right now, where by far the most common way of getting Java 8 support is using RetroLambda, which uses local classes for lambdas. Similarly, the Jack compiler would compile lambdas down to local classes for older runtimes (but the Jack compiler has been dropped by Google). – Avi Cherry Sep 11 '17 at 01:47
  • Thanks for the answer. It helped me a lot. – Matheus Bento Sep 11 '17 at 11:24
  • One more question: Can someone tell me why am I returning true inside the touchDown method? I know that it requires it because it has Boolean as a return type but why? If the code is executed when the event happens, why do I need to return true? Is there a situation where i would want to return false? This class extends from a class that implements InputProcessor, requiring me to define the touchDown method and it returns false. Why? – Matheus Bento Sep 11 '17 at 11:37
  • I suggest you read the [javadocs here](https://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputListener.html#touchDown-com.badlogic.gdx.scenes.scene2d.InputEvent-float-float-int-int-). If you return false, you won't receive touchDrag or touchUp events, plus the event won't be considered 'handled' and a parent component may get a chance to handle it instead. – Avi Cherry Sep 11 '17 at 11:58
  • @AviCherry As written, your answer is still wrong. Java does not specify how lambdas will be implemented in byte code. Your answer implies that it does. In principle, even RetroLambda might change its implementation at some point, although I agree that's not likely. – Brick Sep 11 '17 at 19:17