Showing posts with label scope. Show all posts
Showing posts with label scope. Show all posts

Wednesday, November 11, 2015

Stop giving me so much static

Let's talk about the keyword static for a moment.

There are two different uses for this in our code.  It can be applied to methods or it can be applied to variables.

When static is applied, it disassociates the item being declared from any specific instance of the class.  For methods, this means that the method can be called by anyone at any time without creating a new class of that type.  This can be particularly handy for utility functions, initializers, or any other chunk of code that wants to run without caring that it's about a particular person, bill, or list of stocks.  For variables, it mostly means that the value is shared across all instances of the class.

Static Methods

Static methods can be called without making a new class.  For instance, it could be used for a factory method, which is a design pattern that calls for objects to be built by calling a function to do so.

public class Person {
    ...
    public static Person createPerson(firstName, lastName) {
        ...
    }
}

You can call createPerson without having a Person object, like so:

Person.createPerson("John", "Doe");

Many utility functions are declared as static, and as a general rule they will need to operate free of any context supplied by objects.

Static Variables

Static variables come from the same basic idea of being disassociated from any specific instances of the class.  They're useful for maintaining overall context, for instance, if we want to keep track of how many Person objects we've created we could write something like this:

public class Person {
    ...
    private static int numberCreated = 0;
    ...
    public static Person createPerson(firstName, lastName) {
        ...
        numberCreated ++;
        ...
    }
}

Now, every time we invoke createPerson, we'll also be incrementing the value of numberCreated, and it will always be available as a statistic our programs can check.

Many times, inexperienced programmers will overuse static methods, because when they first start creating methods and functions, they just call them from main.  Your main method has to be static, and if you try calling non-static methods directly from it, the compiler complains.  So the path of least resistance is often seen as just making those other methods static too.  This does seem to work, but it goes against object oriented design principals, and it's best to nip this in the bud.  So they start with this:

public class OutputTest {
    ...
    private PrintStream outputStream = ...;
    ...
    public static void main(String [] args) {
        print("This is a test");
    }
    ...
    public void print(String message) {
        outputStream.println(message);
    }
}

But that fails because you can't call 'print' from a static context, so they change it to this

public class OutputTest {
    ...
    private PrintStream outputStream = ...;
    ...
    public static void main(String [] args) {
        print("This is a test");
    }
    ...
    public static void print(String message) {
        outputStream.println(message);
    }
}

That fails, too, because outputStream isn't static, so they change the code again:

public class OutputTest {
    ...
    private static PrintStream outputStream = ...;
    ...
    public static void main(String [] args) {
        print("This is a test");
    }
    ...
    public static void print(String message) {
        outputStream.println(message);
    }
}

And they're satisfied.  Everything works now!  I call this the static cascade, and I recommend avoiding it.  If you did not originally intend for these items to be shared and free of binding to specific objects, then don't go changing them to act that way.  

Get used to writing object oriented code.  Do this instead:

public class OutputTest {
    ...
    private PrintStream outputStream = ...;
    ...
    public static void main(String [] args) {
        OutputTest test = new OutputTest();  //The key!
        test.print("This is a test");
    }
    ...
    public void print(String message) {
        outputStream.println(message);
    }
}

While it might be a bit more of a conceptual leap, it's actually a far smaller change to the original code, and it opens your class up to being more adaptable to use in other systems.  It's also a necessary step towards object oriented thinking, so you might as well get it over with now.

It's the final countdown!

Many times we declare a variable that we really ought not to change, at least in some particular spot.  We can just remember not to change it, and that might work.  A better solution is to let the language keep us from even trying, and Java offers us the final keyword for this purpose.

A variable declared as final cannot be changed, so maybe the word 'variable' does not even really apply, so we can call them 'constants' if we like.  This has a couple of different purposes.  First off, we might want to set up a constant like an approximation of pi for use in a class that does geometric calculations:

static final float pi = 3.14159;

Now any code that tries to change pi will be flagged by the compiler as a problem.

Another use for the final keyword is to make sure that methods don't modify parameters we've given them:

public void calculateValue (final int quantity) {

This would make sure that we don't accidentally change quantity somewhere inside the calculateValue method.

The compiler is able to simplify the code that it generates for variables declared as 'final', so we get that benefit, too.  In short, it's a good idea to declare things as final whenever it's reasonably possible to do so.

Tuesday, February 10, 2015

It's a SERIES OF TUBES!

I've been struck in recent days by a number of posts over at /r/javahelp, all with the same basic theme.  They've got some code, and it references a variable name that is declared elsewhere in the program.  These nascent developers don't yet understand that just because you mention a variable in one place, that doesn't mean it's available elsewhere.

It is fundamentally important to understand the issue of scope if you ever hope to write programs well.  Where you declare things, and what keywords you use when you do so, have a huge effect on the visibility of your variables.

For instance, I often see issues that boil down to an attempt at doing this:

void multiplyTwoNumbers(int numberOne, int numberTwo) {
    int numberThree = numberOne * numberTwo;
}

"It doesn't work" they say, or "it doesn't do anything".  My friends, it does exactly what you told it to.  It's just that you didn't tell it to do anything particularly useful.  When this method is finished, it will have multiplied numberOne by numberTwo and assigned its value to numberThree.  Then it's done.

Some folks go a bit further:

void callingMethod() {
    int numberThree;
    multiplyTwoNumbers(5,10);
    System.out.println("See?  It didn't work! " + numberThree);
}

And they're right, it didn't.

The reason is that the two variables named 'numberThree' are entirely independent of each other.  One of them is really 'callingMethod->numberThree' and the other one is 'multiplyTwoNumbers->numberThree' and they have never met.

Naming things the same is meaningless.  It might help us to understand our code better, but as far as Java is concerned the variables might as well be called Ben and Jerry.  Mmmm, Ben and Jerry.

If you really want callingMethod->numberThree to take on the value you calculated in 'multiplyTwoNumbers' you need to explicitly lay it out.  Just as you can't expect your sink to drain properly into the sewer system if you don't actually provide a continuous connection, you can't expect what one method does to just be known by another method.

So make sure you declare multiplyTwoNumbers as an int.  Make sure you actually return the value of numberThree at when it's done:

int multiplyTwoNumbers(int numberOne, int numberTwo) {
    int numberThree = numberOne * numberTwo;
    return numberThree;
}

That will help.  You've now installed the pop-up drain thingie in the bottom of the sink bowl.  Let's finish the job:

void callingMethod() {
    int numberThree;
    numberThree = multiplyTwoNumbers(5,10);
    System.out.println("It verks! " + numberThree);
}

This is so important to understand.  Local variables go away as soon as the local block (code between { and }) comes to and end.  If you don't provide a method for the value to escape the block, it's going to go to the big heap in the sky.

Perhaps part of the problem is that people get confused by the fact that they can do this:

public class Wizz {

    int wizzLevel = 0;

    public void bumpWizzLevel() {
        wizzLevel = wizzLevel + 1;
    }

    public void displayWizzLevel() {
        System.out.println("Current level: " + wizzLevel);
    }
}

Yes, that's perfect valid.  In this case, 'wizzLevel' is an instance variable of the class Wizz.  Every copy of Wizz you create has its own wizzLevel, and all methods within Wizz can reference or change it.

The key of course, is to look at the brackets.  Note that 'int wizzLevel' is inside the brackets for the class as a whole, so it's in scope everywhere within the class.

The key takeaway is that when you want to understand where  a variable is available to you, you need to understand that so long as you are still inside the same set of brackets where it was declared you should be good to go.

{
    int outside;
    {
        int inside; //outside is still available
        {
            int wayInside; //inside and outside are still available
        }
        //wayInside is gone
    }
    //inside is gone
}
//outside is gone