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

Sunday, December 21, 2014

Old McDonald had a File, IO IO IO

One of the most basic and common functions we programmers have to implement involves reading and writing data files. So get used to doing so. The good news is that Java is well equipped with many library functions for doing this. The bad news is exactly the same. There are many library functions, so many that you can get lost in them. In fact, just when we thought we had a handle on the 'java.io' package, along came 'java.nio' (New I/O) to cloud the picture even more. Even an experienced programmer can get a little lost in the multiple classes that are in place for dealing with persistent data, and I certainly don't want to overwhelm my audience, so I'm going to stick to some basics.

 First off, I'm going to pretend that 'new I/O' never happened. I'm also going to offer you only a subset of all the functionality that's out there even in the java.io library. When it comes down to it, when you've got a file to read or write, you want to be able to just do so, without a lot of muss or fuss.

I am going to have to assume that you're reasonably familiar with directories (or folders, if you must) and files as seen by your operating system. I'm going to assume that you understand what a text file is, and that you feel reasonably comfortable with viewing them or editing them with notepad, or UltraEdit, or whatever your choice of text editor might happen to be.

First, a note on file types. There are of course a huge number of file formats out there, to support applications from AutoCAD to PhotoShop to Visual Studio, but all files can be pretty much divided into to major categories: Text and Other. Text files are broken down into lines, and many programs deal with them on a line by line basis. Other files tend to consist of plain old blocks of information, whether those be pixels for an image, or a song or movie, or quite literally anything else you can save.

Java supports both major file types of course, and has objects specifically for dealing with each. When dealing with text files it's very convenient to read or write things one line at a time, so we have that ability in our libraries.

We've used an object we call 'PersonData' before, and we'll make use of it again now.  Suppose we had a file in which we had a bunch of last names and first names stored, for now we'll assume that last names and first names each appear on a line, so the file would look something like this:

PEOPLE.TXT

    Schmoe
    Joe
    Doe
    John
    Smith
    Bill

So we've got the information here to support three PersonData objects, all contained within a file called PEOPLE.TXT.

If we wanted to read the names from this file and create PersonData objects, it's conceptually pretty simple:

Open the file for reading
As long as there's something to read
    Read a last name
    Read a first name
    Create a PersonData object with those values

That makes sense, right?

Enter the Scanner.  The Scanner object is pretty new, and frankly I'm still not completely used to it.  But it's so darned handy I have no excuse not to use it.  The Scanner has several different constructors, but we're going to make use of the one that uses the File object right now.

First, let's define the file that we intend to read.  This sets us up for the next step:

File personFile = new File("PEOPLE.TXT");

Note that this isn't something that will fail, even if PEOPLE.TXT does not actually exist.  Maybe we intend to create it next, or we want to test for its existence.  The File object is handy that way.

Scanner personScanner = new Scanner(personFile);

This can fail.  If personFile refers to a non-existent file bad things can and will happen starting here.  Proper code will be wrapped up in try/catch blocks as I discussed when we talked about exception handling.

At this point we have covered the first line of our pseudo-code algorithm:  "Open the file for reading".  The next part was "As long as there's something to read", which a Scanner can handle quite easily:

while (personScanner.hasNextLine()) {

This loop will now run until we're out of lines.  That's pretty convenient.  Now let's extract the last and first names:

    String lastName = personScanner.nextLine();
    String firstName = personScanner.nextLine();

Yes, there's a weakness or three here.  We're making the assumption that our file is correctly formatted and complete, with two lines for every person.  Good coding practice would include checking 'hasNextLine' in between those two lines and dealing with it if that isn't the case.

We're actually almost done here.  Once we've got the last name and the first name, we can go ahead and create a PersonData object:

    PersonData personFromFile = new PersonData( firstName, lastName );

At this point, technically we can end the loop.  We've done the specified job, although realistically this is useless.  There's not much point in reading a data file only to toss away the data.  You'd want to do something with these 'personFromFile' objects, like adding them to a List or Map, or storing them in some other sort of data structure your program will use.

But for now, we'll just pretend we're doing that and close down the file:

}
Scanner.close();

And that's about all there is to reading text files since Java 7 came out.  It was a bit more difficult before, when we had to create a couple of other objects in place of Scanner, and if the data included numbers or multiple items on one line it was even more involved.

Let's say that the file was formatted like this instead:

PEOPLE.TXT

    Schmoe Joe
    Doe John
    Smith Bill

That seems reasonable, right?  It is, and it's pretty easy to deal with.  All we'd need to do is change the following two lines from this:

    String lastName = personScanner.nextLine();
    String firstName = personScanner.nextLine();

To this:

    String lastName = personScanner.next();
    String firstName = personScanner.nextLine();

And we will have accomplished our goal.  We could also have done this:

    String lastName = personScanner.next();
    String firstName = personScanner.next();
    String anythingLeftover = personScanner.nextLine();

What is important is to realize that 'nextLine' is critical to advance us from one line to the next, so we have to use it before we go through the loop again.

Before I go, I also want to mention that Scanner is capable of reading not just Strings, but also any of the primitive data types as well.  It supports nextInt(), nextDouble(), etc.  It's also capable of reading more complicated input via the use of Regular Expressions, which are a huge topic in their own right.  Regular Expressions essentially provide an entire language for matching complicated text, and an entire series of articles could no doubt be written just on that topic alone.

In our next episode, we'll talk about writing text files, which is just the reverse of what we've done here.