Showing posts with label declaration. Show all posts
Showing posts with label declaration. Show all posts

Wednesday, March 7, 2012

Generics in Java

Generics are an oddly-named thing in Java that associate objects stored in a Collection with a particular type. Let's take a look at a piece of code that uses a Collection to store some Strings. It uses an ArrayList. Here's the code:


import java.util.*;
public class NoGenerics{
// This is an example of what happens
// when you don't declare a class
// for a Collection using Generics.
// It won't compile!

public static void main(String[] arg){
// No Generic declaration.
ArrayList myList = new ArrayList();
String myString="Boss Moss";
String yourString="Snorkledorf";

// put the strings into the ArrayList
myList.add(myString);
myList.add(yourString);

for (String aString: myList){
// We can't do String methods on the items from myList.
if (aString.startsWith("Boss")){
System.out.println(aString + ":");
System.out.println("Make sure you spell it right!");
}
else{
System.out.println(aString + ":");
System.out.println("I am so pretty!");
}
}
}
}

What happens here to upset javac is that once the String is placed in the ArrayList, its type is lost, and it is known only as an Object. To be treated as a String again, it would have to be cast as a String. If any errors are made in casting the data, the program will fail at runtime, not when it is being compiled--when the programmer has it in their hands to fix it.

Feel free to take the above code and try to compile it to see the error you'll get. It is instructive.



The Fix for Lost Types

In Java version 1.5 "Generics" were added to fix this. Now Collections like the ArrayList can associate a type with the data put in them, and the compiler can do type checking. Here's the above code, with the Generic declaration added:


import java.util.*;

public class Generics{
// This is an example of what happens
// when you declare a data class
// for a Collection using Generics.

public static void main(String[] arg){
// Generic declaration.
ArrayList myList = new ArrayList();
String myString="Boss Moss";
String yourString="Snorkledorf";

// put the strings into the ArrayList
myList.add(myString);
myList.add(yourString);

for (String aString: myList){
// Now when we can do String methods on the List items.
if (aString.startsWith("Boss")){
System.out.println(aString + ":");
System.out.println("Make sure you spell it right!");
}
else{
System.out.println(aString + ":");
System.out.println("I am so pretty!");
}
}
}
}

Generics = Typed Collections
So if you are going to use a collection on Objects all of a specific type (rather than mixing and matching), put on a Generic declaration.
StumbleUpon

Tuesday, January 25, 2011

Java's File Names and Class Names

Java is picky about the file names you use.

Each source file can contain one public class. The source file's name has to be the name of that class. By convention, the source file uses a .java filename extension (a tail end of a file name that marks the file as being of a particular type of file.)

So, for a class declared as such:

public class HelloWorld{
...

The file it is stored in should be named HelloWorld.java.

The capitalization should be the same in both cases. Some operating systems don't notice whether file names are capitalized or not. It doesn't matter, you should be in the habit of using the correct capitalization in case you work on a system that does care about capitalization.

When you compile your file with javac, you pass the full file name to javac:

javac HelloWorld.java

Let's say we save the file under a different name. We write the following program:

public class HelloThere{
public static void main(String arg[]){
System.out.println("Hello There.");
}
}

Then we save it as HelloWorld.java, and try to compile it:

>javac HelloWorld.java
FileName.java:1: class HelloThere is public, should be declared
in a file named HelloThere.java
public class HelloThere{
^
1 error


Java lets us know that it won't compile until we rename the file appropriately (according to its rules.)

So let's rename the file. Let's call it HelloThere.javasource. Seems a bit more explicit than just .java, right? Let's run the compiler:


>javac HelloThere.javasource
error: Class names, 'HelloThere.javasource', are only accepted
if annotation processing is explicitly requested
1 error

Java's still not happy with us. Annotation processing? That's when we include extra information in the program about the program itself. We're not bothering with that just now. So we should just name the file HelloThere.java, and not get fancy with our file names.

But, under the right circumstances, javac does allow file name extensions other than .java. That's why we always type in the full file name, including .java, when we use javac. We say 'javac HelloThere.java', not just 'javac HelloThere'. Javac can't assume that we mean a .java file, though that's what it will usually be.

The Class File

Once we make javac happy with a proper file name, and a program with no errors, javac produces a new file. This file will have the original file name, but with .java replaced with .class. This is your bytecode file, the file that the Java Virtual Machine can run.

When we run the program with Java, we're running the .class file. In the case of HelloThere, we're running the HelloThere.class file. But we don't type in the full file name. Why?

Unlike javac, java requires a .class file. That's all it will work with. There's no opportunity to have a different extension to the file name. So it assumes the .class part of the file name. But that's not the whole story.

If you add .class yourself, here's what you'll get:

>java HelloThere.class
Exception in thread "main" java.lang.NoClassDefFoundError:
HelloThere/class
Caused by: java.lang.ClassNotFoundException: HelloThere.class
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)

Pretty ugly. What we're actually doing when we type "java HelloThere" is telling Java to run the class HelloThere. Java assumes that it will find this in a file called "HelloThere.class", so that's what it's looking for first.

We're not telling Java to run the file HelloThere.class, we're telling it to run the class HelloThere, which it expects to find in the file HelloThere.class.

But what if we ask for another class that doesn't have its own .class file?

Just for fun, let's change HelloThere.java like this, and see what happens:
public class HelloThere{
public static void main(String[] arg){
System.out.println("Hello.");
}
}

class HelloZoik{
public static void main(String[] arg){
System.out.println("Zoiks!");
}
}

After we edit it, we compile with 'javac HelloThere.java' and hold our breath.

Hurray! No errors!

Now we have a second class, HelloZoik, in the HelloThere.class file. Can Java find it?

Let's try:
 java HelloZoik
Zoiks!

It worked! Java found our class inside HelloThere.class.

This shows it's not the file name that we're calling with the 'java' command, it's the class name.

If Java doesn't find the class inside a file with the same name as the class followed by .class, it'll look in the other .class files available.
StumbleUpon

Friday, August 8, 2008

Reference Types: Names and Objects

When we learned about primitive variables we learned that they are a way of storing a simple value, like a number or a true-false state. We give them a name, and whenever we want to get that value we use that name. Whenever we want to change the value that's stored, we set the name equal to the new value:
int value;     //Declare an integer variable named value.
value=100; // Store the number 100 in value.
System.out.println(value); // Print the number in value, "100"
value=20; // Put 20 in value. Over-writes the 100.
count=value; // Gets the number from value and puts
// it in count.
// Assumes 'count' was declared as an int before.

When we use objects, we're using a more complex kind of variable. We don't do it for the sake of complexity, but because adding some complexity here makes things a lot simpler elsewhere in our program. A lot simpler.

Object variables are called "reference types", the type is actually the class that the object belongs to. The difference between a reference variable and a primitive variable is that a primitive variable actually is a storage location in computer memory that holds the stored information. A reference variable is a name that names a specific type of information. It doesn't actually hold the information, it's just associated with an object that actually holds the information.

This why there's a two step process in creating a Java variable. The first step, declaration, sets up a name we're going to use for a particular type of object. The second step, initialization, associates that name with an object--creating a new object if necessary.

Let's say I'm talking to a friend. I say, "I'm going to get a hamster and call it Wilmot."

"That's nice," says the friend. "Can I see Wilmot?"

"I don't have him yet."

"What color is Wilmot?"

"I don't know, I don't have a hamster yet."

Then I go to the pet store and pick up a hamster. I call him Wilmot, put him in a cage and take him home. I've now initialized the name Wilmot I declared to my friend earlier to point to a specific hamster.

Now when my friend says "Can I see Wilmot?" I can show him a hamster, and say "This is Wilmot." When he wants to know the color, there is an object to get the color of.

In Java terms, if I had a Hamster class, I could declare a name like this:
Hamster wilmot;

I now have a name for a Hamster object, but no Hamster. I can initialize the reference variable by getting a new Hamster object using the class's constructor, Hamster():
wilmot=new Hamster();

Now I have a Hamster object associated with the name. The name references the Hamster object that's been created. Hence the term "reference variable".

Now, if I create a new name and make it point to the same Hamster object, it becomes a new name for the same object:
Hamster fluffy;
fluffy=wilmot;

Now, if I make any changes to fluffy they also happen to wilmot. For example, if wilmot's size is 10cm, and I set fluffy's size to 12cm, then get wilmot's size it will be 12cm.

This is different from what happens with primitive variables. If I do the same thing with a pair of primitive variables like this:
int value, count;
value=100; // Initialize value to 100;
count=value; // Set count equal to value.
count=count-20; // Subtract 20 from count.
// We could have said 'count-=20;', too.
System.out.println("value= " + value); // Print value.
System.out.println("count= " + count); // Print count.

Then the output will be:
value= 100
count= 80

With a primitive variable, when we set it equal to another primitive we get a copy of the value in that primitive's own storage. When we set a reference variable equal to another reference variable, we are now referring to the same object as the other reference variable. In pet terms, it's like giving our hamster a second name. We might call it wilmot or fluffy, but they're both the same hamster.

If we need to have each name apply to a different hamster, we need to go back to the pet store for another hamster. In Java, we need to use the constructor to get another Hamster object:
Hamster wilmot, fluffy;
wilmot=new Hamster();
fluffy=wilmot; // fluffy and wilmot are now both names
// for the same Hamster object. Anything we do to fluffy
// will also apply to wilmot, and vice versa.
fluffy=new Hamster();
// Now fluffy applies to a different Hamster object.
// We can make changes to fluffy and
// they won't affect wilmot. We now have two
// Hamster objects, each with its own name
// to refer to it.

But what if we want it to act like a primitive object? What if we want a copy of wilmot to make changes to without affecting wilmot? Then we use a method of the object's class to create a new object that's a copy of that object. Usually this will be clone():
Hamster wilmot, fluffy;
wilmot=new Hamster();
fluffy=wilmot.clone();

Now fluffy is a copy of wilmot, and we can make changes to fluffy without affecting wilmot. Note: whether a class has the clone() method varies from class to class. Normally, if the class implements the Clonable interface then it has a clone() method that works this way.
StumbleUpon

Thursday, July 10, 2008

The void Type

'Pierre


void can be the most confusing type. It's an odd word to start with. It's part of the long string of words that appear before the word main in the declaration of that method.

Void means 'empty' or 'nothing'. In the case of a Java method, it means that that method returns nothing:

public void drawTriangle(){ ...

This is as opposed to a method with another type, which returns an object of that type:

public int sum(int a, b){
return a + b;
}

This returns an int object to the caller:

int count1, count2, amt;
...
amt=sum(count1, count2);

Why would you want a method that returns nothing? Because often you have methods that do something that doesn't affect the state of the program itself. Drawing something is an example of this. A method that draws something makes its results obvious in what it draws. It doesn't need to pass any information back to the other parts of the program. Instead it is expressing the state of the program to the outside world.
StumbleUpon