Java Tech: Generics and You
| ||||||
How much do you know about generics? Based on your amount of generics knowledge, are you comfortable with having to maintain another developer's generified code, should your project manager tell you to do so? Perhaps you have not taken the time to determine how much you know about generics and how much you still have to learn. If you would like to assess your generics I.Q., you might find this article a helpful test of your generics knowledge.
Note: This article bases its content on a very useful and extensive generics FAQ that I discovered a couple of months ago. After you finish this article, do yourself a favor by checking out the FAQ, listed in the Resources section at the end of the article.
This section presents a test on several generics concepts. The test's 20 questions are a mixture of fill in the blank, true/false, multiple choice, and long answer. Take all the time you need to complete this test, but resist the urge to peek at the answers (which I present in the next section) until you finish. The test begins now.
Generics identifies language features for defining and using ________________ and ________________.
What is a parameterized type?
How does generics promote type safety?
Which of the following reference types cannot be generic?
True or false: Set<Object> is the supertype
of Set<String>.
What is a type parameter?
A ________________ describes a family of types and is used as a type parameter.
How is a concrete parameterized type different from a non-concrete parameterized type?
True or false: parameterized types have type parameters.
Which syntax would you use to express a wildcard with a lower
bound of some type?
?? extends type? super typeIdentify those types that cannot be used as type arguments.
Why can you not derive (directly or indirectly) generic classes
from Throwable?
What is type erasure?
Identify the tasks performed by type erasure.
True or false: The parameterized types List<Date>
and List<?> share the same runtime type.
The opposite of type erasure is known as ________________.
Which overloaded method is called by the following program?
// Overload.java
import java.util.Date;
public class Overload
{
public static void someOverloadedMethod (Object o)
{
System.out.println
("call to someOverloadedMethod(Object o)");
}
public static void someOverloadedMethod (Date d)
{
System.out.println
("call to someOverloadedMethod(Date d)");
}
public static <T> void methodCaller (T t)
{
someOverloadedMethod (t);
}
public static void main (String [] args)
{
methodCaller (new Date ());
}
}
What is an "unchecked" warning message?
When the compiler reports an unchecked or unsafe operation, it also tells you to recompile with the ________________ option to obtain details.
True or false: the code fragment System.out.println (o instanceof
Set<String>); will compile.
|
Now that the test is over, it's time to find out how well you understand generics. This section repeats the questions stated in the previous section, but it also presents the answers (in red) to those questions. Review those answers and compare them to your responses. Give yourself one mark for each correctly answered question. If you believe you got a question partially right, give yourself half of one mark. Add up the marks and multiply by 5 to receive your test score.
Generics identifies language features for defining and using generic types and generic methods.
What is a parameterized type?
A parameterized type is an instance
of a generic type. Example: HashMap<K,V> is a
generic type and HashMap<String,Double> is an
instance of that type--a parameterized type.
How does generics promote type safety?
Prior to J2SE 5, Java's Collections
framework did not provide for homogeneous
collections--collections whose elements all share the same
type. Collections held Object references, raising the
possibility of heterogeneous collections--collections
with elements of different types. Collection APIs revealed this
heterogeneous bent by accepting arbitrary elements for insertions,
and by returning Object references when retrieving
elements from collections.
The problem with heterogeneous
collections: lack of type safety. Because a retrieval operation
requires a cast operation, to downcast from Object to
the returned object's type, before assigning the returned object to
a variable of that type, there is a potential for a
ClassCastException, should an object not match or
subtype that type. The following code fragment illustrates this
problem:
List integers = new ArrayList ();
integers.add (new Integer (25));
integers.add (new Integer (-367));
integers.add (BigInteger.ONE);
Iterator it = integers.iterator ();
while (it.hasNext ())
{
Integer i = (Integer) it.next ();
// Do something with i
}
The code fragment is problematic because
of the (Integer) cast. That cast signifies our
assumption that integers is a homogeneous collection
of Integer objects. But that is not the case: there is
nothing to prevent us from adding BigIntegers (or
other kinds of objects) to integers. When retrieving
the BigInteger from the collection, that cast results
in a thrown ClassCastException: we cannot cast from
BigInteger to Integer.
To avoid ClassCastExceptions,
we need to prevent non-Integer objects from being
added to the List. Generics lets us accomplish that
task. Consider the following code fragment:
List<Integer> integers = new ArrayList<Integer> ();
integers.add (new Integer (25));
integers.add (new Integer (-367));
integers.add (BigInteger.ONE);
Iterator<Integer> it = integers.iterator ();
while (it.hasNext ())
{
Integer i = it.next ();
// Do something with i
}
Unlike the previous code fragment, which
uses the raw List type to create an object
representing a heterogeneous List, this code fragment
uses generics to create a parameterized
List<Integer> type. That type's
integers object represents a homogeneous
List of Integers. The compiler, which has
access to this type information, displays an error message when
encountering integers.add (BigInteger.ONE);:
List<Integer> has no method for adding a
BigInteger to a List of
Integers.
Because integers has
the List<Integer> type, its iterator()
method returns an object of Iterator<Integer>
type. This object's next() method will only return
Integer objects; a cast is not required (which reduces
source code clutter) and a ClassCastException cannot
occur. By providing static type information, so that the compiler
can enforce a homogeneous List of
Integers, generics has made this code
type-safe.
Which of the following reference types cannot be generic?
True or false: Set<Object> is the supertype
of Set<String>.
False. There is no type relationship between different instances of the same generic type for different concrete type arguments.
What is a type parameter?
A type parameter is a stand-in for
a type argument. For example, the E in public
class TreeSet<E> is a type parameter. It will be
replaced with a type argument when creating an instance of a
generic type. Example: new
TreeSet<String>.
A wildcard describes a family of types and is used as a type parameter.
How is a concrete parameterized type different from a non-concrete parameterized type?
A concrete parameterized type cannot have wildcards as its type arguments. Wildcards are permitted for non-concrete parameterized types.
True or false: parameterized types have type parameters.
False. Parameterized types, which are instances of generic types, have type arguments.
Which syntax would you use to express a wildcard with a lower
bound of some type?
?? extends type? super
typeIdentify those types that cannot be used as type arguments.
All primitive types, such as
int, char, and double,
cannot be used as type arguments. In contrast, all references
types, including parameterized types (such as
ArrayList<Set<String>>), can be used as
type arguments.
Why can you not derive (directly or indirectly) generic classes
from Throwable?
Generics-based exception and error classes are not allowed because the exception-handling mechanism is JVM-based and the JVM knows nothing about generics.
What is type erasure?
Type erasure is a compiler activity where all instances of a generic type or a generic method are mapped to the unique bytecode representation of the generic type or generic method.
Type erasure 1) elides type parameters
(upon encountering a generic type definition or a generic method
definition, the compiler removes all type parameters and replaces
them with their leftmost bound or the Object type, if
there is no bound), and 2) elides type arguments (upon encountering
a parameterized type, the compiler removes all type
arguments--List<Integer> is translated to
List, for example).
True or false: the parameterized types List<Date>
and List<?> share the same runtime type.
True. All parameterized versions of
List (including the List raw type) are
represented by the same Class object, which serves as
the runtime type.
The opposite of type erasure is known as reification.
Which overloaded method is called by the following program?
// Overload.java
import java.util.Date;
public class Overload
{
public static void someOverloadedMethod (Object o)
{
System.out.println
("call to someOverloadedMethod(Object o)");
}
public static void someOverloadedMethod (Date d)
{
System.out.println
("call to someOverloadedMethod(Date d)");
}
public static <T> void methodCaller (T t)
{
someOverloadedMethod (t);
}
public static void main (String [] args)
{
methodCaller (new Date ());
}
}
Although you might think otherwise, the
program above calls someOverloadedMethod(Object o).
This method, instead of someOverloadedMethod(Date d),
is called because overload resolution happens at compile time, when
the generic method is translated to its unique bytecode
representation, and type erasure (which takes care of that mapping)
causes type parameters to be replaced by their leftmost bound or
Object (if there is no bound). After type erasure, we
are left with the following non-generic method:
public static void methodCaller (Object t)
{
someOverloadedMethod (t);
}
What is an "unchecked" warning message?
Occasionally, you may encounter a compiler
warning message stating that an operation is unchecked. For
example, you may be told about an unchecked call to an
add(E) method. This "unchecked" warning message is due
to the compiler and runtime system not having sufficient type
information to ensure type safety.
The use of raw types (especially in legacy code) serves as the most likely culprit of "unchecked" warning messages. Because raw types don't provide enough type information, it is impossible for the compiler to perform all of its needed type checks. Consider the code fragment below:
Map colors = new HashMap ();
colors.put ("red", Color.red);
The code fragment concerns itself with
mapping AWT-based Color objects to descriptive names,
which are represented by String objects. When you
compile this code, the compiler reveals the following warning
message:
warning: [unchecked] unchecked call to put(K,V)
as a member of the raw type java.util.Map
Map's put()
method invocation causes a problem for the compiler. It does not
know if it's safe to put an entry, consisting of a
String-based key and a Color-based value,
into the Map. The raw Map type's lack of
specific type information for its entries makes this call unsafe,
and so the compiler reports the warning. Of course, changing
colors' declaration to Map<String,Color>
colors = new HashMap<String,Color> (); provides this
information; the warning is no longer necessary.
When the compiler reports an unchecked or unsafe operation, it also tells you to recompile with the -Xlint:unchecked option to obtain details.
True or false: the System.out.println (o instanceof
Set<String>); code fragment will compile.
False. The code fragment will not compile
because Set<String> does not have an exact
runtime type representation. Following type erasure, the runtime
type becomes the raw type Set. Because
instanceof can only check against Set,
and not Set<String>, confusion results. To
prevent this confusion, the compiler disallows the code
fragment.
After obtaining your test score, you might be feeling depressed or elated. If you received a low score, this does not mean you are a loser when it comes to generics. Instead, the score means you have lots of learning to do; this is important if you might be called upon to maintain another developer's generified code. On the other hand, receiving a high score does not mean you have little else to learn about generics. All tests present samples of subject material, and this test is no different. If you are deficient in a category that the test has not covered, that deficiency will not be revealed by the test. Regardless of your score, I hope that you have found this test to be enlightening, and I hope that it enhances your understanding of generics.
The previous Java Tech article presented you with some challenging homework on BlueJ. Let's revisit that homework and investigate solutions.
Create an applet first by clicking the project toolbar's New
Class... button, and next by selecting the resulting Create New
Class dialog box's Applet radio button. Although you can set
breakpoints in the applet, they will have no effect during the
applet's execution in a web browser or in the
appletviewer--those tools have their own virtual machines, and
those virtual machines don't recognize breakpoints.
Jeff Friesen is a freelance software developer and educator specializing in Java technology. Check out his site at javajeff.mb.ca.
Read more Java Tech columns.
|
|