|
|
|
Advertisement
|
instanceof and Can you dance()?
The primary use of instanceof is to find out whether you
can perform some kind of operation on an object. In the next
example, class DancingHippopotamus (a subclass of
Hippopotamus) declares a dance() method. The
makeHipposDance() method of class Example2
assumes it is receiving a collection of hippopotami, but not necessarily
DancingHippopotamus objects. It uses instanceof to find out whether each object in the collection is an instance of DancingHippopotamus objects. For each instance of DancingHippopotamus it finds, it downcasts the reference to DancingHippopotamus and invokes dance().
// In file rtci/ex2/Hippopotamus.java
class Hippopotamus {
private String yawnAdjective;
Hippopotamus(String yawnAdjective) {
this.yawnAdjective = yawnAdjective;
}
void yawn() {
System.out.println(yawnAdjective + " yawn!");
}
}
// In file rtci/ex2/DancingHippopotamus.java
class DancingHippopotamus extends Hippopotamus {
DancingHippopotamus(String yawnAdjective) {
super(yawnAdjective);
}
void dance() {
System.out.println("Dance!");
}
}
// In file rtci/ex2/Example2.java
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
class Example2 {
public static void main(String[] args) {
ArrayList hippos = new ArrayList();
hippos.add(new Hippopotamus("Big"));
hippos.add(new DancingHippopotamus("Little"));
hippos.add(new Hippopotamus("Technicolor"));
makeHipposYawn(hippos);
makeHipposDance(hippos);
}
// Client must pass a collection of Hippopotami
static void makeHipposYawn(Collection hippos) {
Iterator it = hippos.iterator();
while (it.hasNext()) {
Hippopotamus hippo = (Hippopotamus) it.next();
hippo.yawn();
}
}
// Client must pass a collection of Hippopotami
static void makeHipposDance(Collection hippos) {
Iterator it = hippos.iterator();
while (it.hasNext()) {
Object hippo = it.next();
if (hippo instanceof DancingHippopotamus) {
DancingHippopotamus dh = (DancingHippopotamus) hippo;
dh.dance();
}
}
}
}
The previous example highlights the primary legitimate use of
instanceof: to find out whether an object can do something
for you (in this case, dance). When instanceof returns
true, the reference is downcast to a more specific type so
methods can be invoked.
A good general rule is that you should strive not to write code that
doesn't know what's coming, but sometimes you can't avoid it. In such
cases, instanceof is the preferred way to find out whether
you've got a specific type you'll want to treat specially.
Another legitimate, though less common, way to use
instanceof is to check for a tag interface. Sometimes an
interface is defined without any members, merely to serve as a "tag" for
classes. For example, the Serializable interface contains
no fields or methods. Serializable's sole purpose is to
indicate that an object allows serialization.
When you use instanceof to check for a tag interface, you are not so
much asking an object Can you do a particular thing for me? but
rather, Can I do a particular thing to you? Instead of determining
whether the object has a particular method you want to invoke, you find
out whether you are "allowed" to use the object in some way that
doesn't necessarily involve invoking its methods.
Class 'Class'
Where casts, instanceof, and instance method invocations
give you a taste of runtime class information, class
java.lang.Class gives you a feast: Instances of class
java.lang.Class represent the most abundant source of
runtime class information in the JVM. Once you have a reference to an
object's Class instance, you have access to a wealth of
information about that object's class.
You can get a reference to any object's Class instance by
invoking getClass() on the reference, as in:
static void printClassInfo(Object o) {
System.out.println("------------------------");
Class c = o.getClass();
// ...
If you know the type at compile time, you can get a reference to the
class instance for a type with the syntax:
<typename>.class. For example, the
expression Hippopotamus.class would yield a reference to
the Class instance for class Hippopotamus.
Once you have a reference to a Class, you can get at all
kinds of information about the represented type, some of it via the
reflection API.
Here's an application that accesses and prints some of the information available from Class objects:
// In file rtci/ex2/Hippopotamus.java
class Hippopotamus {
private String yawnAdjective;
Hippopotamus(String yawnAdjective) {
this.yawnAdjective = yawnAdjective;
}
void yawn() {
System.out.println(yawnAdjective + " yawn!");
}
}
// In file rtci/ex2/DancingHippopotamus.java
class DancingHippopotamus extends Hippopotamus {
DancingHippopotamus(String yawnAdjective) {
super(yawnAdjective);
}
void dance() {
System.out.println("Dance!");
}
}
// In file rtci/ex3/Example3.java
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.lang.reflect.*;
class Example3 {
public static void main(String[] args) {
ArrayList hippos = new ArrayList();
hippos.add(new Hippopotamus("Big"));
hippos.add(new DancingHippopotamus("Little"));
hippos.add(hippos);
hippos.add(new String("Hi There!"));
Iterator it = hippos.iterator();
while (it.hasNext()) {
printClassInfo(it.next());
}
}
static void printModifiers(int modifiers) {
if (Modifier.isPrivate(modifiers)) {
System.out.print("private ");
}
else if (Modifier.isProtected(modifiers)) {
System.out.print("protected ");
}
else if (Modifier.isPublic(modifiers)) {
System.out.print("public ");
}
if (Modifier.isAbstract(modifiers)) {
System.out.print("abstract ");
}
if (Modifier.isStatic(modifiers)) {
System.out.print("static ");
}
if (Modifier.isFinal(modifiers)) {
System.out.print("final ");
}
if (Modifier.isTransient(modifiers)) {
System.out.print("transient ");
}
if (Modifier.isVolatile(modifiers)) {
System.out.print("volatile ");
}
if (Modifier.isSynchronized(modifiers)) {
System.out.print("synchronized ");
}
if (Modifier.isNative(modifiers)) {
System.out.print("native ");
}
}
static void printClassInfo(Object o) {
System.out.println("------------------------");
Class c = o.getClass();
System.out.println("Name: " + c.getName());
Class superclass = c.getSuperclass();
if (superclass != null) {
System.out.println("Superclass: " + superclass.getName());
}
System.out.println("Interfaces:");
Class[] interfaces = c.getInterfaces();
for (int i = 0; i < interfaces.length; ++i) {
System.out.println("\t" + interfaces[i].getName());
}
System.out.println("Fields:");
Field[] fields = c.getDeclaredFields();
for (int i = 0; i < fields.length; ++i) {
System.out.print("\t");
printModifiers(fields[i].getModifiers());
System.out.println((fields[i].getType()).getName()
+ " " + fields[i].getName() + ";");
}
System.out.println("Methods:");
Method[] methods = c.getDeclaredMethods();
for (int i = 0; i < methods.length; ++i) {
System.out.print("\t");
printModifiers(methods[i].getModifiers());
System.out.print((methods[i].getReturnType()).getName()
+ " " + methods[i].getName() + "(");
Class[] params = methods[i].getParameterTypes();
for (int j = 0; j < params.length; ++j) {
System.out.print(params[j].getName());
if (j != 0 && j != params.length - 1) {
System.out.print(", ");
}
}
System.out.println(");");
}
}
}
Here's the output of the Example3 application:
------------------------ Name: Hippopotamus Superclass: java.lang.Object Interfaces: Fields: private java.lang.String yawnAdjective; Methods: void yawn(); ------------------------ Name: DancingHippopotamus Superclass: Hippopotamus Interfaces: Fields: Methods: void dance(); ------------------------ Name: java.util.ArrayList Superclass: java.util.AbstractList Interfaces: java.util.List java.lang.Cloneable java.io.Serializable Fields: private transient [Ljava.lang.Object; elementData; private int size; Methods: private void RangeCheck(int); public void add(intjava.lang.Object); public boolean add(java.lang.Object); public boolean addAll(intjava.util.Collection); public boolean addAll(java.util.Collection); public void clear(); public java.lang.Object clone(); public boolean contains(java.lang.Object); public void ensureCapacity(int); public java.lang.Object get(int); public int indexOf(java.lang.Object); public boolean isEmpty(); public int lastIndexOf(java.lang.Object); private synchronized void readObject(java.io.ObjectInputStream); public java.lang.Object remove(int); public java.lang.Object set(intjava.lang.Object); public int size(); public [Ljava.lang.Object; toArray(); public [Ljava.lang.Object; toArray([Ljava.lang.Object;); public void trimToSize(); private synchronized void writeObject(java.io.ObjectOutputStream); ------------------------ Name: java.lang.String Superclass: java.lang.Object Interfaces: java.io.Serializable java.lang.Comparable Fields: private [C value; private int offset; private int count; private static final long serialVersionUID; public static final [Ljava.io.ObjectStreamField; serialPersistentFields; public static final java.util.Comparator CASE_INSENSITIVE_ORDER; Methods: public char charAt(int); public int compareTo(java.lang.Object); public int compareTo(java.lang.String); public int compareToIgnoreCase(java.lang.String); public java.lang.String concat(java.lang.String); public static java.lang.String copyValueOf([C); public static java.lang.String copyValueOf([Cint, int); public boolean endsWith(java.lang.String); public boolean equals(java.lang.Object); public boolean equalsIgnoreCase(java.lang.String); public [B getBytes(); public void getBytes(intint, [B, int); public [B getBytes(java.lang.String); private [B getBytes(sun.io.CharToByteConverter); public void getChars(intint, [C, int); public int hashCode(); public int indexOf(int); public int indexOf(intint); public int indexOf(java.lang.String); public int indexOf(java.lang.Stringint); public native java.lang.String intern(); public int lastIndexOf(int); public int lastIndexOf(intint); public int lastIndexOf(java.lang.String); public int lastIndexOf(java.lang.Stringint); public int length(); public boolean regionMatches(intjava.lang.String, int, int); public boolean regionMatches(booleanint, java.lang.String, int, int); public java.lang.String replace(charchar); public boolean startsWith(java.lang.String); public boolean startsWith(java.lang.Stringint); public java.lang.String substring(int); public java.lang.String substring(intint); public [C toCharArray(); public java.lang.String toLowerCase(); public java.lang.String toLowerCase(java.util.Locale); public java.lang.String toString(); public java.lang.String toUpperCase(); public java.lang.String toUpperCase(java.util.Locale); public java.lang.String trim(); public static java.lang.String valueOf(char); public static java.lang.String valueOf(double); public static java.lang.String valueOf(float); public static java.lang.String valueOf(int); public static java.lang.String valueOf(long); public static java.lang.String valueOf(java.lang.Object); public static java.lang.String valueOf(boolean); public static java.lang.String valueOf([C); public static java.lang.String valueOf([Cint, int);
Well, that's a lot of information. To get at this information, the
Example3 class uses java.lang.Class and the
reflection API. Other aspects of the reflection API can be used not
just to get information about a type, but to actually invoke methods or
alter fields.
So how can you take advantage of java.lang.Class and
reflection in your designs? In general, you should avoid using
reflection in your designs. Reflection is intended for special cases
like JavaBeans builder tools, object serialization mechanisms, object
inspectors, debuggers, and so on. Reflection is useful in situations in
which you have absolutely no idea what's coming. When a programmer
drops a JavaBean into a bean builder, for example, the builder hasn't a
clue about the class of the bean. The builder must use reflection to
introspect the bean class to discover its class, its properties, its
methods, and so on. Because the bean builder has no idea what's coming,
it must use reflection to figure out how to use the beans it is given.
One situation in which you may know what's coming, but will still want
to use java.lang.Class and possibly the reflection API, is
when you are creating an instance of a class loaded into a different
name space. One of the main reasons for using class loaders and
dynamic extension is so your program doesn't have to know at compile
time all the classes and interfaces it will load and use at runtime.
Although you should prefer designs in which you assume that dynamically
loaded objects will implement a known interface or descend from a known
class, in order to instantiate an object of the dynamically loaded class, you'll
still have to use either Class.newInstance() or the
newInstance() method of class
java.lang.reflect.Constructor().
Whenever possible, you should design programs such that they know
what's coming to a sufficient extent that objects can be used without
consulting instanceof or reflection. In cases where you
need to ask an object, Can you do a particular thing for me?, you
should choose instanceof over reflection to make the
query.
|
Sponsored Links
|