Free Java Tutorials >> Table of contents >> Function Declarations

1. Static Void Function Declarations

Creating functions

A function is a block of code that can be run. We have seen how to run functions such as Math.abs(-1), and here is the first function we will write besides void main:

public class MyProgram {
    public static void main(String[] args) {
		doSomeThing(); //This is a "function call" or "method call"
		doSomeThing(); //This is also a function call
		doSomeThing(); //We sometimes call these "method executions"
    }
	
	//This is a function declaration:
	static void doSomeThing() { 
		System.out.println("Rainbow Dash");
	}
	//(the function declaration has ended)
}

If you run this program, you will see that it outputs "Rainbow Dash" three times. The program starts in the public static void main. Then, each doSomeThing() is a "function call" which jumps down to the function declaration. Then, when the function finishes running all its lines, the execution jumps back to where we were in main.

The syntax of a function call is function_name( arguments... ). We will learn more about arguments (parameters, inputs) later, so for now a function call is simply function_name(). The parenthesis are required.

Let us see another declaration:

public class MyProgram {
	static void marvin() {
		for(int i = 0; i < 3; i++) System.out.println("The paranoid android");
	}

    public static void main(String[] args) {
		doSomeThing(); //Rainbow Dash
		marvin();      //Prints The paranoid android three times
		doSomeThing(); //Rainbow Dash
    }
	
	static void doSomeThing() { 
		System.out.println("Rainbow Dash");
	}
}

Notice that the order of these declarations is not important (unlike in C or C++). However, it is important that every function is declared within a class, and it is important that functions are not within another function (until we learn about anonymous classes much later).

Why do we write functions?. As your programs get longer, you will want to split off chunks of it into smaller programs. Recall when we constructed algorithms by putting one algorithm in another. We will be doing the same with function calls, which will allow us to understand and write our program in small chunks.

The syntax of a function declaration

At bare minimum, a function declaration has the syntax RETURN_TYPE function_name(){}. In the case of the above examples, we have the two functions with the names "main" and "doSomeThing". The return type for both functions is "void", which means nothing. We will learn more about return types below.

static: In addition, you will notice that both main and doSomeThing have the word "static" in front of them. We will not fully appreciate this keyword until we learn about Classes. Specifically, static means that the function can be run without an associated Object. For now, every function you will write needs the static keyword.

public: The main function has another keyword in front of it, "public". Later on, when we have programs with multiple files and multiple classes, we will use these visibility modifiers. Visibility modifiers control what is allowed (permitted) to run a function. For now, you can leave out the keyword, which is called using the default visibility.

We will learn more about what the String[] args means below.

The call stack

When Java starts a program, it looks for the public static void main(String[] args), and jumps to the first line in that function. Then, when the program calls another function, Java keeps track of the path that it took to reach the function. This allows Java to jump back to the caller function when a function finishes. Here is an example where a function calls a function:

public class MyProgram {
    public static void main(String[] args) {
		marvin();      //Prints Paranoid, then Android three times
    }
	
	static void marvin() { 
		for(int i = 0; i < 3; i++) {
			paranoid();
		}
	}
	
	static void paranoid() {
		System.out.println("Paranoid");
		System.out.println("Android");
	}
}

We can follow the execution of this function:

//01. Call main()
//02. [main()] Call marvin()
//03. [main()] [marvin()] Enter loop, i = 0
//04. [main()] [marvin()] Call paranoid()
//05. [main()] [marvin()] [paranoid()] Output Paranoid
//06. [main()] [marvin()] [paranoid()] Output Android
//07. [main()] [marvin()] [paranoid()] Exit paranoid()
//08. [main()] [marvin()] Iterate Loop(), i = 1
//09. [main()] [marvin()] Call paranoid()
//10. [main()] [marvin()] [paranoid()] Output Paranoid
//11. [main()] [marvin()] [paranoid()] Output Android
//12. [main()] [marvin()] [paranoid()] Exit paranoid()
//13. [main()] [marvin()] Iterate Loop(), i = 2
//14. [main()] [marvin()] Call paranoid()
//15. [main()] [marvin()] [paranoid()] Output Paranoid
//16. [main()] [marvin()] [paranoid()] Output Android
//17. [main()] [marvin()] [paranoid()] Exit paranoid()
//18. [main()] [marvin()] Iterate Loop(), i = 3
//19. [main()] [marvin()] Exit Loop()
//20. [main()] [marvin()] Exit marvin()
//21. [main()] Exit main()
//22. Program ends

Indeed, you can use a "debugger" to follow the program execution while it is running.

2. Parameters

Functions that take inputs (aka "arguments","parameters")

Functions can also take inputs, which means that you can take variables and pass them from one function to another. Let's look at this function timestwo:

public class MyProgram {
    public static void main(String[] args) {
		timestwo(21);
		timestwo(42);
		int bla = 5;
		timestwo(bla);      //you can use any expression that evalutes to an int
    }
	
	static void timestwo(int x) {  //you declare the parameters in the parenthesis
		System.out.println(x*2);       //outputs x times two
	}
}

So timestwo takes one input, an int. Within timestwo, this int is called x, but the variable only exists when running timestwo. Another way to think about this is: each time you run timestwo, it creates an int x that is used for that function call (execution) only, and it stops existing once the function exits

Once you have made timesto take a parameter int x, whenever you run timestwo you must give it an int expression in the parenthesis. You cannot give it any other variable type, and there must be exactly one integer expression.

Let's see another example, this time with a String argument:

public class MyProgram {
    public static void main(String[] args) {
		badjoke("man");      //A man walked into a bar. Ow!
		String x = "stinky";
		badjoke(x+" panda"); //A stinky panda walked into a bar. Ow!
    }
	
	static void badjoke(String x) {  //you declare the parameters in the parenthesis
		System.out.println("A "+x+" walked into a bar. Ow!");
	}
}

Multiple arguments / parameters

A function can take several arguments in its parenthesis, which must be separated by commas:

public class MyProgram {
    public static void main(String[] args) {
		repeat("Don't Panic", 3);
		repeat("Mostly Harmless", 2);
    }
	
	static void repeat(String message, int numTimes) {  
		for(int i = 0; i < numTimes; i++) {
			System.out.println(message);
		}
	}
	//Program outputs Don't Panic three times
	//Then it outputs Mostly Harmless twice
}

When there are multiple arguments, the function declaration must specify the type for each argument. Even if both inputs are of type int, you cannot do myfunction(int x,y); you must do myfunction(int x, int y). Additionally, the order of the parameters is important.

Advanced note: pass by value

For C or C++ developers, it is important to note that Java functions are all pass-by-value, not pass by reference. However, because all array and object variables are pointers ("references") in Java, passing arrays and objects copy the reference that points to the same object in memory.

3. Return Values and Examples

Functions that can return outputs

Functions can not only take input, but they can also provide a "return value". If you type "void" in front of a function, then it does not give anything back as output. However, you can change the word void to variable type:

Difference between return and println: System.out.print outputs text to the screen. Return, on the hand, gives information back to whatever called the current function. A program with no println statements will show nothing to the user, but functions can still pass information with return in that case.

public class MyProgram {
    public static void main(String[] args) {
		int x = awesome();
		System.out.println(x + 1); //prints 43
    }
	
	static int awesome() {
		return 42;
	}
}

In the above example, awesome() is actually being used as an integer expression. When you say int x = awesome(), it runs awesome. The only line in awesome() is return 42, which tells awesome to exit immediately and give back the value 42 (it "returns" 42). Now the first line in main is the same as int x = 42;. Here's another example:

public class MyProgram {
    public static void main(String[] args) {
		println(isawesome(42)); //prints true
		println(isawesome(5));  //prints false
		int x = 42;
		if(isawesome(x)) {
			println("Don't forget your towel!");
		}
		if(isawesome(x)) {
			println("Let's take a trip to frogstar");
		}
	}
	
	static boolean isawesome(int x) {
		return x == 42; 
	}
}

Here isawesome can be used wherever we need a boolean. Depending on what is passed into isawesome, it evalutes to either true or false. Specifically, if the number passed in is equal to 42, then it evalutes to true.

Returning Early: The prime check function

Let's say we need to check a bunch of numbers if they are prime. Now we can enclose the prime checking in a function:

public class MyProgram {
    public static void main(String[] args) {
		int[] nums = {41, 42, 43, 7, 2, 10};
		for(int i = 0 ; i < nums.length; i++) {
			if(isPrime(nums[i])) {
				println(nums[i]+" is prime!");
			} else {
				println(nums[i]+" is not prime!");
			}
		}
	}
	
	static boolean isPrime(int x) {
		for(int i = 2 ; i*i <= x; i++) {
			if(x%i==0) return false;
		}
		return true;
	}
}

This prime checker is similar to ones we have seen. It checks numbers between 2 and the square root of x to see if any are factors of x. Once any x%i==0 evalutes to true, the isPrime function returns false. This exits immediately with the value of false. As you can see, making functions can really help organize your code, and it makes the void main portion of the program much shorter!

Return is required for non-void functions

Once a function has a return type besides void, you must supply a return value. Thus, the following code is incorrect:

public class MyProgram {
    public static void main(String[] args) {
		System.out.println(myFunction());
	}
	
	static int myFunction() {
		if(5 > 2) {
			return 42;
		}
	}
}

You may think: five is greater than two, so doesn't myFunction always return true? Unfortunately, Java (and all other computer languages) cannot effectively guess what a function will return. Thus, you must rewrite the previous program as one of these two functions:

//Example 1. This compiles:
static int myFunction() {
	if(5 > 2) {
		return 42;
	}
	return 0;
}
//Example 2. Java also knows that this function always returns
//This is since either the if or else will return, regardless of 5>2:
static int myFunction() {
	if(5 > 2) {
		return 42;
	}else {
		return 0;
	}
}

Returning early in a void function

You can also use return in void functions. Return will exit the function, and you do not need to supply a value:

public class MyProgram {
    public static void main(String[] args) {
		example(10); //prints Boooo!
		example(42); //prints Hooray!
	}
	
	static void example(int x) {
		if( x != 42 ) {
			System.out.println("Boooo!");
			return;
		}
		System.out.println("Hooray!");
	}
}

4. Functions calling functions

Calling functions from other functions

Take a look at the prime checker function above. It uses x%i==0 to determine if two numbers are divisible. We could have made that into a function, and called it like so:

public class MyProgram {
    public static void main(String[] args) {
		int[] nums = {41, 42, 43, 7, 2, 10};
		for(int i = 0 ; i < nums.length; i++) {
			if(isPrime(nums[i])) {
				println(i+" is prime!");
			} else {
				println(i+" is not prime!");
			}
		}
	}
	
	static boolean isPrime(int x) {
		for(int i = 2 ; i*i <= x; i++) {
			if(isDivisible(x,y)) return false; //use isDivisible
		}
		return true;
	}
	static boolean isDivisible(int x, int y) {
		return x%i == 0;
	}
}

So each time we call isPrime, it also ends up calling isDivisible many times. Although this example was short enough that we didn't really need to create the isDivisible function, often splitting things up can make your code simpler to understand.

Functions with the same name as other functions: overloading

We can have two functions with the same name as long as their declaration is different:

public class MyProgram {
    public static void main(String[] args) {
		System.out.println(bla(42));   //prints 84, or 42*2
		System.out.println(bla("42")); //prints 4, or "42".length()*2
	}
	
	static int bla(int x) {
		return x*2;
	}
	static int bla(String x) {
		return x.length()*2;
	}
}

Java can tell which function we are calling by the type of the argument we are passing. Since the first argument, 42, is an int, we call the first bla. Sames goes for the second function that takes a String. We can even have functions with the same name that take a different number of arguments:

public class MyProgram {
    public static void main(String[] args) {
		System.out.println(isOdd(-3));        //this one doesn't handle negatives
		System.out.println(isOdd(-3, true));  //this one optionally handles negatives
		System.out.println(isOdd(-3, false)); //same as #1
	}
	
	static boolean isOdd(int x, boolean handleNegatives) {
		if(handleNegatives) return isOdd(Math.abs(x));
		else return isOdd(x);
	}
	static boolean isOdd(int x) {
		return x%2==1;
	}
}

Previous: String Functions

Next: Recursion