The type is bound to the object. Therefore, the type of containers can change. An
imperfect rule of thumb is that static languages force you to declare variables, but
dynamic languages don't.
Ironically, most dynamic languages also tend to be strongly typed. Most weakly
typed languages tend to be static. Said another way, strong typing can be dynamic
or weak, but weak typing is usually also static. You don't find many weakly and
dynamically typed languages, beyond Assembly language. Figure 4-1 places
programming languages on two axes. Java has strong, static typing. You know this,
because you get type mismatch errors when you make certain kinds of mistakes.
Compiling this:
class TypeTest {
public static void main(String args[ ]) {
i = 4; // Nope!!! Static typing
int j;
j = 4.2; // Nyet!!! Strong typing
}
}
gives you this result:
TypeTest.java:3: cannot resolve symbol
symbol : variable i
location: class TypeTest
i = 4;
TypeTest.java:5: possible loss of precision
found : double
required: int
j = 4.2;
Figure 4-1. Java is a strongly and statically typed language
Sometimes, that's good. After all, a bug that gets caught at compile time takes
much less time than a bug that gets solved much later. In general, though, the
4.3.5. Code/Compile Cycle
Dynamic typing comes into play especially when you need to experiment.
Remember, you must declare variables in statically typed languages. In Java, that
means you need to start each application with a class definition, and it snowballs.
You can't just jump in and evaluate a single line of codethe compiler just doesn't
have enough information. Instead of just simply evaluating statements, you need to
blow out a class, type everything, compile, and execute. In Smalltalk, Lisp, Basic,
and Ruby, you can just start typing. For simple experimentation on an initial cut at
a Fibonacci sequence, here's the Java version:
class Fib {
public static void main (String args[ ]) {
int x1 = 0;
int x2 = 1;
int total = 1;
for (int i=0; i<10; i++) {
System.out.println(total);
total = x1+x2;
x1 = x2;
x2 = total;
}
}
}
It's 13 lines; 41 words; 226 characters. Keep in mind that Java forces you to
declare the class to explore, and that's what we're measuring here. On the
command line, you need to save, compile, and run. The Ruby counterpart looks
like this:
x1 = 0
x2 = 1
100.times do
puts x2
type supports all of the messages of the old type, you will likely limit the changes
to one place in your code.
4.3.7. Generics
The Java architects have traditionally gone to great lengths to ensure type safety,
but there's been one particular case that's troubled them. When you take an object
from a collection, you need to cast the object:
ArrayList animals = new ArrayList( );
animals.add("elephant");
String cat = (String)animals.get(0);
The compiler has just lost the ability to provide compile-time type safety. You
could call the array element anything you want. To fix this, Java introduces an ugly
implementation of a feature called generics . Here's what the usage looks like:
ArrayList<String> animals = new ArrayList<String>( );
animals.add("elephant");
String elephant=animals.get(0);
Comparing the preceding code with its nongeneric equivalent, you may think that
you avoided casting, but you really did not. Java introduced an ugly
implementation of generics, called type erasure. Under the hood, in the modified
version, the ArrayList still maintains a collection of Objects and not a
collection of Strings. Of course, any library that you need to strongly type with
user-defined types must enable the code for generics. Enabling generics gets a little
ugly. Here's the List declaration from within the Java collections package:
public interface List<E> { void add(E x);
Iterator<E> iterator( );
}public interface Iterator<E> {
E next( );
boolean hasNext( );
}
If you're not a fan of statically typed languages, you don't like the extra type checks
that place yet an additional burden on you. Even if you like the idea of generics,