Received: from SOUTH-STATION-ANNEX.MIT.EDU by po10.MIT.EDU (5.61/4.7) id AA00507; Tue, 14 Mar 00 16:07:54 EST
Received: from hermes.javasoft.com by MIT.EDU with SMTP
	id AA28359; Tue, 14 Mar 00 16:07:37 EST
Received: (from nobody@localhost)
	by hermes.java.sun.com (8.9.3+Sun/8.9.1) id VAA09934;
	Tue, 14 Mar 2000 21:03:24 GMT
Date: Tue, 14 Mar 2000 21:03:24 GMT
Message-Id: <200003142103.VAA09934@hermes.java.sun.com>
X-Authentication-Warning: hermes.java.sun.com: Processed from queue /bulkmail/data/ed_67/mqueue2
X-Mailing: 195
From: JDCTechTips@sun.com
Subject: JDC Tech Tips  March 14, 2000
To: JDCMember@sun.com
Reply-To: JDCTechTips@sun.com
Errors-To: bounced_mail@hermes.java.sun.com
Precedence: junk
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
X-Mailer: Beyond Email 2.2


 J  D  C    T  E  C  H    T  I  P  S

                      TIPS, TECHNIQUES, AND SAMPLE CODE


WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips, 
March 14, 2000. This issue covers:

         * Using java.lang.Class
         * Overload Resolution
         
These tips were developed using Java(tm) 2 SDK, Standard Edition, 
v 1.2.2.

You can view this issue of the Tech Tips on the Web at 
http://developer.java.sun.com/developer/TechTips/2000/tt0314.html.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

USING JAVA.LANG.CLASS

In object-oriented programming, a class is a user-defined type, 
with a set of methods that operate on instances (objects) of the 
class. For example, you might have a class Point in an application:

    public class Point {
        int x;
        int y;
    }

and the application operates on instances of Point, like (37,47)
and (153,89).

java.lang.Class is one of the standard classes in the Java
programming language, and instances of java.lang.Class represent 
other classes and interfaces. This tip could use Class to refer 
to java.lang.Class, but that terminology might be confusing. 
So you'll see java.lang.Class throughout the discussion.

java.lang.Class gives you the ability to find out information 
about classes that are currently loaded into the Java(tm) Virtual
Machine* (JVM). It also gives you a way to dynamically load 
additional classes into your application. Here's an example, 
it's a short application that uses java.lang.Class features:

    public class ClassDemo1 {
        public static void main(String args[]) {
            Class cls = null;

            // load a class by name (like java.util.ArrayList)

            try {
                cls = Class.forName(args[0]);
            }
            catch (ClassNotFoundException exc) {
                System.err.println(exc);
            }

            // display the hierarchy back to java.lang.Object

            do {
                System.out.println(cls);
                cls = cls.getSuperclass();
            } while (cls != null);
        }
    }

This program uses Class.forName to load a class into the JVM; if the
class is already loaded, it finds the java.lang.Class instance that 
represents the class. Then the program calls Class.getSuperclass
repeatedly to find the class's superclass. The result of this
process is the class hierarchy for a given class. For example, if
you invoke the program with an argument of "java.util.ArrayList",
the output is:

    class java.util.ArrayList
    class java.util.AbstractList
    class java.util.AbstractCollection
    class java.lang.Object

which is the class hierarchy of ArrayList up through 
java.lang.Object. In other words, you can find out about the class 
properties of ArrayList from within a running program. 
java.lang.Class is a class whose instances represent other Java 
classes and interfaces, such as ArrayList, String, and Cloneable.

java.lang.Class doesn't have a public constructor, so you can't say:

    Class c = new Class(...);

Instead, you obtain java.lang.Class instance references using
Class.forName as illustrated above. Class.forName is used to load 
a class at run time, based on a string containing the class name 
(such as "java.util.ArrayList").

Another way you can obtain a java.lang.Class instance reference is 
to use the ".class" notation, as in:

    Class c = java.lang.String.class;

Because Java arrays fit into the class hierarchy rooted at
java.lang.Object, you can also say:

    Class c = int[].class;

A final way to obtain a java.lang.Class reference is through an 
object reference. In this case, you say:

    Class c = obj.getClass();

or:

    int vec[] = new int[10];
    Class c = vec.getClass();

Once you have a java.lang.Class instance reference, you can create
new instances of the class that it represents. Here's an example:

    public class ClassDemo2 {
        public static void main(String args[]) {
            Class cls = null;
            Object obj = null;

            // load a class by name

            try {
                cls = Class.forName(args[0]);
            }
            catch (ClassNotFoundException exc) {
                System.err.println(exc);
            }

            // create a new instance of that class

            try {
                obj = cls.newInstance();
            }
            catch (IllegalAccessException exc1) {
                System.err.println(exc1);
            }
            catch (InstantiationException exc2) {
                System.err.println(exc2);
            }
        }
    }

Class.newInstance is used to create new instances of the class
represented by the java.lang.Class instance. It assumes the
existence of a default (no parameter) constructor for the class. 
You could ask why you can't simply use "new" to create a new
class instance, and not go to all the trouble of using
java.lang.Class and newInstance. The key point about the above
approach is that it's dynamic; you can load classes by name into 
a program and create new instances of these classes.

If you load classes using a string name, like "pkg.classname", then
you might wonder how the loaded classes are manipulated. One way is
to program in terms of interfaces. Suppose that you have an 
interface A, and two classes B and C that implement that interface. 
Class B uses an implementation that is the most space efficient, 
while class C uses more space but runs faster than B. You can 
dynamically load B or C, but then program in terms of the 
interface A. So the line above that reads:

    obj = cls.newInstance();

might better read:

    A aref = null;

    ... load B or C by name ...

    aref = (A)cls.newIntance();

A complete example looks like this:

    // file A.java

    public interface A {
        void f();
    }

    // file B.java

    public class B implements A {
        public void f() {
            System.out.println("B.f");
        }
    }

    // file C.java

    public class C implements A {
        public void f() {
            System.out.println("C.f");
        }
    }

    // file ClassDemo3.java

    public class ClassDemo3 {
        public static void main(String args[]) {
            A aref = null;
            Class cls = null;

            // load a class (B or C)

            try {
                cls = Class.forName(args[0]);
            }
            catch (ClassNotFoundException exc) {
                System.err.println(exc);
            }

            // create a new instance of that class

            try {
                aref = (A)cls.newInstance();
            }
            catch (IllegalAccessException exc1) {
                System.err.println(exc1);
            }
            catch (InstantiationException exc2) {
                System.err.println(exc2);
            }

            // call f() in the loaded class

            aref.f();
        }
    }

If you say:

    $ java ClassDemo3 C

then the output is:

    C.f

that is, the f method in the loaded class C is called through 
the interface reference.

In ClassDemo1 above, getSuperclass finds the superclass of
a class represented by a java.lang.Class instance. There are
other query methods that you can use with java.lang.Class. For
example, a common one is illustrated by this program:

    public class ClassDemo4 {
        public static void classify(Object obj) {
            if (obj == null) {
                System.out.println("null");
            }
            else if (obj.getClass().isArray()) {
                System.out.println("array");
            }
            else {
                System.out.println("non-array");
            }
        }
        public static void main(String args[]) {
            classify(null);

            classify(new int[10]);

            classify(new String());
        }
    }

In this example there's an object reference passed to the classify
method, and you'd like to know whether it represents an array.

A final area worth mentioning is reflection. The Java system
defines a separate package for reflection (java.lang.reflect), but
it relies on methods found in java.lang.Class. One way to
distinguish java.lang.reflect facilities from those in
java.lang.Class is to note that the java.lang.reflect operates 
on individual fields and methods in a class instance. 
java.lang.Class is concerned with a class as a whole unit. 
For example, this program displays a list of all the public methods 
found in String:

    import java.lang.reflect.*;

    public class ClassDemo5 {
        public static void main(String args[]) {
            Class cls = java.lang.String.class;
            Method methlist[] = cls.getMethods();
            for (int i = 0; i < methlist.length; i++)
                System.out.println(methlist[i]);
        }
    }

java.lang.reflect.Method is a class found in the reflection package.
Class.getMethods gets a list of all the methods in the
class. You could go deeper into the internals of a method, for 
example, you could obtain a list of all the method's parameters in 
a form that could be manipulated within an application. To do that, 
you would use facilities such as Method.getParameterTypes.

In other words, in this simple demo program, the names of all 
methods and their parameters are displayed. However, to find out 
which of the methods has a third parameter of type "int", use 
reflection facilities within java.lang.reflect.Method.

java.lang.Class is a powerful mechanism for examining the properties
of classes, and for dynamically loading and manipulating those
classes.


OVERLOAD RESOLUTION

Suppose that you're doing some Java programming, and you have code
like this:

    public class OverDemo1 {
        static void f(float f) {}
        static void f(double d) {}
        public static void main(String args[]) {
            f(37);
        }
    }

What happens here? It's possible to convert an integer value (37) 
to either a float or a double, so which of the f methods is called?
Or is this program invalid? (The equivalent C++ program gives a
compile error.) The answers to these questions relate to
"overload resolution," that is, the rules that are applied
to determine which method is called when there is a set of 
identically-named methods.

The first rule says that a method is "applicable" in a given
overloading case if the number of parameters in the method
declaration matches the number of arguments in the method
invocation, and the type of each argument can be converted to the
type of the corresponding method parameter.

You might think that a rule stating that the number of parameters
and arguments must match would be obvious, but not necessarily so. 
In C++, functions are allowed default parameters, so that you can 
declare a function by saying:

    void f(int, double = 12.34);

and then call it with:

    f(37);

which is converted by the C++ compiler to:

    f(37, 12.34);

The rule about conversions can be illustrated by a slight variation
on the first example:

    public class OverDemo2 {
        static void f(float f) {}
        static void f(String s) {}
        public static void main(String args[]) {
            f(37);
        }
    }

In this example, 37 can be converted to a float through a "widening
primitive conversion." But there's no corresponding way to convert
37 to a String, so there's no issue of choosing one f method over
the other. The compiler chooses f(float).

The second rule is that a method declaration must be "accessible,"
that is, available at the point where the method is invoked. Here
is another example:

    class A {
        public static void f(float f, double d) {}
        private static void f(double d, float f) {}
    }

    public class OverDemo3 {
        public static void main(String args[]) {
            A.f(37, 47);
        }
    }

At the point where A.f is invoked within main, only one of the 
f methods from A is accessible; the other one is private and not
available outside of the A class. This particular example will
trigger an ambiguity error if both f methods are public. Note that
it's never an error to simply declare more than one method with the
same name but different parameter types. The error occurs at the
point of method invocation if the compiler cannot determine which
method to invoke.

The third and final rule is the trickiest. Suppose that after the
first two rules above are applied, there are still two or more
methods with the same name that could possibly be called. For
example, in the OverDemo1 program above, neither of the two rules
already described removes either of the f methods from 
consideration. Both methods have the right number of parameters, 
it's possible to convert 37 to a float or a double, and both 
methods are accessible.

So the third rule is to choose the most "specific" method. The 
rule is: if any method still under consideration has parameter 
types that are assignable to another method that's also still 
in play, then the other method is removed from consideration. 
This process is repeated until no other method can be eliminated. 
If the result is a single "most specific" method, then that method 
is called. If there's more than one method left, the call is 
ambiguous.

Suppose that you have the methods:

    f(float)

    f(double)

In this case, the parameter types for the first method are 
assignable to the parameter types of the second method, that is,
it's legal to say:

    double = float

through a widening primitive conversion. By contrast, saying:

    float = double

is not valid without a cast. Based on this third rule, f(double) is
removed from the set of possible methods to call, and therefore
f(float) is called. You can confirm this behavior with another demo
program:

    public class OverDemo4 {
        static void f(float f) {System.out.println("float");}
        static void f(double d) {System.out.println("double");}
        public static void main(String args[]) {
            f(37);
        }
    }

which prints the value "float" when you run it.

Suppose that you have another case, with two methods:

    f(float, double)

    f(double, float)

and a call:

    f(37, 47)

Here, the first parameter type of the first method (float)
can be assigned to the first parameter type of the second method
(double). But this doesn't work for the second parameter; you
can't assign double to float. If you start with the second 
parameter and go in reverse order, you find the opposite to be 
true. That is, the first parameter type of the second method 
(double) cannot be assigned to the first parameter type of the 
first method (float). But you can assign the second parameter 
float to double. So neither of the f methods can be removed from 
consideration. Therefore the f(37, 47) call is ambiguous.

In section 15.11.2.2 of the Java Language Specification there's 
a short paragraph that helps explain this rule:

    The informal intuition is that one method declaration is more
    specific than another if any invocation handled by the first
    method could be passed on to the other one without a
    compile-time error.

Another example will help to clarify this rule:

    class A {}

    class B extends A {}

    class C extends B {}

    public class OverDemo5 {
        static void f(A a) {
            System.out.println("f(A)");
        }
        static void f(B b) {
            System.out.println("f(B)");
        }
        public static void main(String args[]) {
            C cref = new C();
            f(cref);
        }
    }

In this example, f(B) is called, because it is more specific than
f(A).  Any invocation of f(B) could also be handled by f(A), but the
reverse is not true.

One final point about overload resolution: it's usually a good idea
to avoid being clever with this mechanism, even if you understand it
thoroughly. If you have a case where the parameter types are
completely distinct, as in:

    void f(int)

    void f(String)

this usage is good, but a case like:

    void f(float)

    void f(double)

called with f(37) starts to get tricky and confusing.

.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .

- NOTE
The names on the JDC mailing list are used for internal Sun
Microsystems(tm) purposes only. To remove your name from the list,
see Subscribe/Unsubscribe below.


- FEEDBACK
Comments? Send your feedback on the JDC Tech Tips to:

jdc-webmaster@sun.com


- SUBSCRIBE/UNSUBSCRIBE
The JDC Tech Tips are sent to you because you elected to subscribe
when you registered as a JDC member. To unsubscribe from JDC email,
go to the following address and enter the email address you wish to
remove from the mailing list:

http://developer.java.sun.com/unsubscribe.html


To become a JDC member and subscribe to this newsletter go to:

http://java.sun.com/jdc/


- ARCHIVES
You'll find the JDC Tech Tips archives at:

http://developer.java.sun.com/developer/TechTips/index.html


- COPYRIGHT
Copyright 2000 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.

This document is protected by copyright. For more information, see:

http://developer.java.sun.com/developer/copyright.html


This issue of the JDC Tech Tips is written by Glen McCluskey.

JDC Tech Tips 
March 14, 2000

*  As used on this document, the terms "Java Virtual Machine" 
  or "JVM" mean a virtual machine for the Java platform.












