We start today's class by attempting to develop a small program
that attempts to explain why elite leapers seem to hang in the air.
We need to read the java.lang.Math
API to help
us solve this problem.
The remainder of the class is spent studying what happens when the client invokes a method.
Elite basketball players seemingly defy gravity by hanging in the air.
In his prime, Michael Jordan's vertical leap was approximately 1.2 meters.
Assuming that the acceleration due to gravity is
g = 9.8 m/s2
, MJ would
have to jump vertically with an initial velocity of
v0 = 4.8497 m/s
to acheive a maximum height of 1.2 meters.
How much time does MJ spend in the top 20 cm of his jump?
Note: The total amount of time MJ spends in the air is
0.9897 s
To solve this problem, I claim that you need to solve the
following equation for
the time x
:
where:
a = (1 / 2) * g
b = -v0
c = 1
Recall that the
quadratic formula
solves for both roots of a quadratic equation.
x1 = | ![]() |
, | x2 = | ![]() |
public class Hangtime { public static void main(String[] args) { final double ACCEL_G = 9.8; final double INITIAL_VEL = 4.8497; } }
What happens a client invokes a method.
When you invoke a method, the compiler needs to determine if the method exists, if the method has been invoked with the correct argument types, and which version of the method should be used.
The compiler uses 3 steps to determine if a method invocation is correct:
Each step can be complicated. We only sketch what happens in the following slides.
The first step the compiler takes is to deduce the class that contains the method. The compiler does this by looking at the type of the class used to invoke the method:
double Math.min(1.0, 2.0);
The compiler deduces that the method invocation belongs to the
class named Math
(because the name
Math
was used before the method name).
The second step the compiler takes is to deduce which version of the method is being invoked. This is necessary because Java allows the implementer to use the same method name for different versions of the method. Methods in a class that share the same name are called overloaded methods.
Consider the family of methods named min
in java.lang.Math
:
static double min(double a, double b)
static float min(float a, float b)
static int min(int a, int b)
static long min(long a, long b)
Here, all of the methods have the same number of parameters but the parameters have different types.
The compiler will search for all methods that are compatible with the method invocation. A compatible method is one that has the same number of parameters as the number of arguments supplied by the client, and the parameter types must match or be compatible with the types of the arguments supplied by the client.
Consider the two methods named nextInt
in java.io.PrintStream
:
void println()
void println(String x)
import java.io.PrintStream; import java.util.Scanner; public class MethodExample2 { public static void main(String[] args) { PrintStream out = System.out; Scanner input = new Scanner(System.in); int first = input.nextInt(); int second = input.nextInt(); out.println(first); out.println(); out.println(second); } }
In the example above, the compiler determines which method to use based on the number of arguments supplied by the client.
Consider the family of methods named min
in java.lang.Math
:
static double min(double a, double b)
static float min(float a, float b)
static int min(int a, int b)
static long min(long a, long b)
public class MethodExample3 { public static void main(String[] args) { double min = Math.min(10.0, 20.0); } }
In the above example, there is only compatible version
of the method named min
, namely
min(double a, double b)
.
public class MethodExample4 { public static void main(String[] args) { int min = Math.min(10, 20); } }
In the above example, all versions
of the method named min
are compatible
based on their signature
because the int
arguments 10
and 20
can be promoted to long
,
float
, or double
.
When there are multiple compatible method signatures, the Java compiler will try to pick the most specific method.
Consider the family of methods named min
in java.lang.Math
:
static double min(double a, double b)
static float min(float a, float b)
static int min(int a, int b)
static long min(long a, long b)
public class MethodExample5 { public static void main(String[] args) { double minDouble = Math.min(10.0, 20.0); int minInt = Math.min(10, 20); } }
static double min(double a, double b)
static float min(float a, float b)
static int min(int a, int b)
static long min(long a, long b)
Which version of min
is selected by the compiler for each
of the following statements?
Math.min( 1F, 2.0F );
Math.min( 1L, 2L );
Math.min( 1, 2L );
Math.min( 1F, 2.0 );
Math.min(float a, float b)
float
Math.min(long a, long b)
long
Math.min(long a, long b)
int
argument can be promoted to
long
but not vice versaMath.min(double a, double b)
float
argument can be promoted to
double
but not vice versaThe final step the compiler performs is to determine if the most specific method is appropriate. One thing that the compiler checks is if a non-static method is called in a static way (which is illegal):
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Scanner;
public class MethodExample6
{
public static void main(String[] args)
{
InputStream in = System.in;
PrintStream out = System.out;
int first = Scanner.nextInt();
}
}
In the example above, the expression in red is illegal
because the method nextInt
is not static and
can only be called using an object.
When you invoke a method, the compiler computes the values of each of the arguments from left to right. The compiler then passes the values (not the variables!) to the method.
Consider the following program:
public class MethodExample7 { public static void main(String[] args) { int x = 10; int y = 20; int z = Math.min(y, x); } }
The method min
receives the values 20
and 10
; it knows nothing about the variables
x
and y
.
int x = 10;
Mean?Recall that the statement int x 10;
reserves
a block of memory large enough to hold an int
,
labels the block with the identifier x
,
and assigns the value 10
to x
.
0 | ||
¦ | ||
main | ||
x ⇒ | 50 | 10 |
¦ |
Math
ClassThe first time that the running program uses the Math
class, the compiled class is loaded somewhere into memory. In our
memory diagram model, the class is shown as a block of memory that
contains the attributes and methods of the class.
0 | ||
¦ | ||
main | ||
x ⇒ | 50 | 10 |
y ⇒ | 20 | |
¦ | ||
¦ | ||
400 | Math class | |
E ⇒ | 2.718281828459045 | |
PI ⇒ | 3.141592653589793 | |
min | ||
a ⇒ | ||
b ⇒ | ||
¦ |
min
When the client invokes the min
method, the values
of the arguments get passed to the method.
0 | ||
¦ | ||
main | ||
x ⇒ | 50 | 10 |
y ⇒ | 20 | |
¦ | ||
¦ | ||
400 | Math class | |
E ⇒ | 2.718281828459045 | |
PI ⇒ | 3.141592653589793 | |
min | ||
a ⇒ | 10 | |
b ⇒ | 20 | |
¦ |
The method returns the value of the result back to the client,
who can then store the result in z
.
0 | ||
¦ | ||
main | ||
x ⇒ | 50 | 10 |
y ⇒ | 20 | |
z ⇒ | 10 | |
¦ | ||
¦ | ||
400 | Math class | |
E ⇒ | 2.718281828459045 | |
PI ⇒ | 3.141592653589793 | |
min | ||
a ⇒ | 10 | |
b ⇒ | 20 | |
result ⇒ | 10 | |
¦ |
Java's mechanism for passing arguments to methods is called pass by value. What is the implication of method only receiving the value of the arguments?
Consider the following method in some hypothetical utility
MakeBelieve
:
static void swap(int a, int b)
Swaps the values of a
and b
.
Parameters:
a
– a value
b
– another value
public class MethodExample8 { public static void main(String[] args) { int x = 10; int y = 20; MakeBelieve.swap(x, y); System.out.print("x : "); System.out.println(x); System.out.print("y : "); System.out.println(y); } }
What does the program print?