subroutines Idea: useful code can be saved and re-used, with different data values Example: Our function to find the largest element of an array might be re-used in many different programs A subroutine allows us to write that functionality in an abstract way so that we, or others, can re-use it without modification
Black-Box function A black box is a metaphor for a device that performs a task, while hiding its inner-workings from the user But still has a usable interface so that a user can change its behavior to fit their needs Example: your car! You don’t need to know how the engine works, just how to turn it on, accelerate, decelerate, and steer Benefits are two-fold: The “user” of the black box doesn’t need to care what’s inside, or how it does its task, just that it works correctly The person who builds (“implements”) the black box doesn’t need to care what the user will do with it Both parties can think abstractly about the task the black box solves
Example: Math.random() We’ve been calling subroutines all along! One of the more useful examples is the random number generator: Math.random() Actually calls a subroutine of a built-in class called “Math” We don’t know anything about it, but trust that it gives us a (mostly) random number between 0 and 1 If our assumptions are correct (and in this case, they are), our programs which rely on it will work the way we think
Java Subroutines All subroutines in java are associated with classes. They are sometimes referred to as “methods” or ”functions” Defined inside the body of a class, with the following syntax: <modifiers> <return-type> <subroutine-name> ( <parameter-list> ) { <body statements> } The modifiers will be keywords like “public”, “private”, and “static” We will learn the distinction later. Our methods for now will all be modified with “public static”
/. The HelloWorld class prints “Hello, World. ”. to the console /** * The HelloWorld class prints “Hello, World!” * to the console. */ public class HelloWorld { public static void main ( String[] args ) { System.out.println(“Hello, World!”); } Function attributes Function arguments Function name Function declaration: defines a function called “main” that: Is public – can be called by other classes Is Static – belongs to the class itself, not a class member Does not return any value (void) Takes a single argument, an array of strings, named “args” Has a following code block in braces
Function definitions The function definition as seen above is like a contract Stipulates the types and number of inputs Guarantees the type of the output Functions can take as parameters any type of data we have seen so far and programmer-defined types we will see later Functions can return any type as well, or nothing (called the void type)
Invoking a subroutine If you have a subroutine called myFunction, which takes no arguments, you can call it from another piece of Java by typing: myFunction() If your function myFunction needs arguments (say, for example, two) then you would type myFunction(arg1, arg2) where arg1 and arg2 are either literals or variables of the correct type specified by the function definition If the function is contained inside a different class, for example MyClass, then you must type MyClass.myFunction(arg1, arg2)
Example: Print divisors of an int Given: a single integer Goal: print out each of its divisors on a single line with spaces between Ex: Given 10 Print: 1 2 5 Use a subroutine called “divisors” Takes a single integer argument
Returning from a subroutine When you invoke a function which returns a value as part of an expression int x = Math.random() * 10 + 1; The function call is ”replaced” by the value “returned” by the function The syntax to stop a subroutine and resume the parent is return <expression>; The parent function “pauses” and waits for the subroutine to finish If there is a return value, it is placed into the expression in the calling function
Return values Use “return <expression>;” to stop the subroutine and return control to calling function The type of the return value must be valid in that expression If the function does not return anything, it has return type void But can still display things and have other side-effects while it runs
Example: Pythagorean theorem Given: two doubles (may have decimal parts), called a and b Goal: compute 𝑎 2 + 𝑏 2 and return the value Ask the user for the two numers
Example: Making the 3N+1 Program into a subroutine Recall the 3N + 1 algorithm: Given a positive integer If the integer is even, divide it by two If the integer is odd, multiply it by 3 and add 1 Repeat until the number is equal to 1 Objective: write this in a function and call it with different arguments Use a nested subroutine call to compute the next number
Function parameters For the sake of discussion, we differentiate between these two instances of “function parameters” public static int myFunc(int x, int y){ … } The parameters of the function definition are called formal parameters (here: x and y) myFunc(a, b); The parameters that actually get passed to a function call are called actual parameters (here: a and b)
Function Parameters Consider the following definition: public static void doTask(int N, double x, boolean test){ ... } We can call the function as: doTask(17, Math.random(), z >= 10); The values of the parameters in the subroutine become: N: 17 x: [some number between 0 and 1] test: [true or false, depending on what z is]
Function definitions We can refer to the signature of a function Ignores parameter names Describes the return and parameter values only If we have function public static void myFunc(int x, double z, boolean b) The signature is: myFunc(int, double, boolean) This is all you need to know to use the function!
Function Overloading When you call a function, the compiler looks at the type of the parameters for a matching function signature E.g. myFunc(17, Math.random(), z >= 10) Will get matched with myFunc(int, double, boolean) We can encounter situations where maybe we want different types of parameters for the same function We can either change the function definition or overload it
Function Overloading We can “protect” the users of our subroutines from extra work E.g. You can call System.out.println() with many different types of arguments System.out.println(50) System.out.println(“Hello, world”) System.out.println(100.7) All work! But the compiler would need three different function signatures!
Function overload We can define functions with the same name but different parameter types The different definitions are said to “overload” the function E.g. public static int funcOne(int n){ … } public static int funcOne(double d){ … } We can now call either funcOne(100) or funcOne (100.5) It will choose whichever matches the type of the parameter!
“Wrapper” functions Used as a “dummy” function to save the user work E.g. we have our Collatz(int) function defined from lab What if a user tries to do Collatz(50.5) ? Let’s say we want to just round off the decimal but do the same work Instead of re-implementing the Collatz algorithm, we can just convert the parameter to an int and call the original! Public static int Collatz(double d){ Return Collatz((int) d); } We call the second function a ”wrapper” function because it’s just used for easy access to the “real” one
Array-valued parameters It is possible to pass an entire array as a single parameter! Public static int biggest(double[] arr){ ... } However, careful! Modifying an array-valued argument within the subroutine will change the original array! This does not happen with regular primitive types