Functions/Methods in Java

Functions/Methods in Java

  • A method is a block of code which only runs when it is called.

  • To reuse the code: define the code once & use it many times

Syntax

<access modifier> <return type> <method name>(<parameter list>) {
    // method body
    return <value>;
}

Now, let's break down this syntax and understand what it means:

  • Access Modifier: A keyword such as public, private, or protected that defines the visibility or accessibility of a method.

  • Return type: The data type of the value returned by the method, or void if the method does not return a value.

  • Method name: This is the name given to the method. It should follow Java's naming conventions.

  • Parameter list: The parameter list of a method is like a set of instructions that tells the method what kind of information it can expect to receive when it's called. It's like a list of variables that the method needs to perform its task.

  • Method Body: The code that performs a specific task when the method is called.

  • Return value: This statement is used to return a value from the method. The <value> should match the data type specified in the <return type>.

Putting it all together, a complete method looks like this

public int addNumbers(int a, int b) {
    // method body
    int sum = a + b;
    return sum;
}

In this example:

  • The access modifier is public.

  • The return type is int.

  • The method name is addNumbers.

  • The parameter list is (int a, int b).

  • The method body calculates the sum of a and b.

  • The method returns an int value (the sum).

Return type:

  • A return statement causes the program control to transfer back to the caller of a method.

  • A return type may be a primitive type like int, char, or void type(returns nothing).


There are a few important things to understand about returning the values:

  • The type of data returned by a method must be compatible with the return type specified by the method

    eg: if the return type of some method is String we cannot return an integer

  •                 public class Example {
                        // This method has a return type of String
                        public String getStringValue() {
                            // Valid return statement
                            return "Hello, World!";
    
                            // Invalid return statement (compilation error)
                            // return 42;
                        }
                    }
    

The variable receiving the value returned by a method must also be compatible with the return type specified for the method

public class Example {
    public String getStringValue() {
        return "Hello, World!";
    }

    public static void main(String[] args) {
        Example example = new Example();

        // Valid usage, as the method returns a String and the variable is of type String
        String result = example.getStringValue();

        // Invalid usage (compilation error), as the method returns a String, but the variable is of type int 
        // int intValue = example.getStringValue();
    }
}

Pass by value

Let us understand Pass by Value from this example:

public class PassingExample {
    public static void main(String[] args) {
        String name = "a";
        ChangeName(name);
    }

    static void ChangeName(String naam) {
        System.out.println(naam);
    }
}

// The output will be a.
  • The value of the name variable (which is a reference to the string object "a") is passed to the ChangeName method.

  • The naam parameter in the ChangeName method receives a copy of the value of the reference (memory address) to the same string object.

Here, both name and naam reference the same string object. If you were to modify the string object through either name or naam, the change would be reflected in both variables. For example, if you were to change the content of the string through naam, it would also affect the content when accessing name afterward.

Name and Naam both reference the same object.

But in the below example even after changing the content of the String through naam it didn't affect the content when accessing name afterward. That's because it is not changing the original object, it's creating a new object. Since it is created inside the function it will not change the original one.

public class PassingExample {

    public static void main(String[] args) {
        String name = "a";      //Name -> a <- Naam
        ChangeName(name);
        System.out.println(name);
    }

    static void ChangeName(String naam) {
        naam = "b";
 //Not changing the original object,just creating a new object
// Naam -> b
// Name -> a 
    }
}

//The output is a.

Name and naam reference to different object in the function scope

Points to be noted:

  • Primitive data types like int, short, char, byte, etc just pass value.

    • These are generally passed by value.

    • When you pass a primitive type to a function or assign it to another variable, a copy of the actual value is passed or assigned.

    • Any changes made to the parameter or variable inside the function or in a different scope do not affect the original value.

void modifyValue(int x) {
    x = 10; // Changes to x don't affect the original value outside the function 
}

int main() {
    int value = 5;
    modifyValue(value);
    // 'value' is still 5 here
}

Object and reference pass value of reference variable

  • When dealing with objects, the situation is a bit different. In languages like Java, references to objects are passed by value.

  • When you pass an object to a function or assign it to another variable, you are actually passing the reference (memory address) to the object.

  • The contents of the object can be modified through this reference, and changes are reflected outside the function.

class MutableObject {
    int value;

    MutableObject(int value) {
        this.value = value;
    }
}

void modifyObject(MutableObject obj) {
    obj.value = 10; // Changes to obj affect the original object outside the function
}

public static void main(String[] args) {
    MutableObject myObject = new MutableObject(5);
    modifyObject(myObject);
    // 'myObject.value' is now 10
}

Scope

Function scope: Variables declared inside a method/function scope (means inside method) can't be accessed outside the method.

public class FunctionScopeExample {

    public static void main(String[] args) {
        myFunction();
        // Uncommenting the line below would result in a compilation error
        // System.out.println(localVar); 
    }

    static void myFunction() {
        int localVar = 10; // localVar is only accessible within myFunction
        System.out.println("Inside myFunction - localVar: " + localVar);
    }
}

Block scope: Variables declared inside a block are only accessible within that block. Once you exit the block, the variables declared within it go out of scope and cannot be accessed anymore.

public class BlockScopeExample {

    public static void main(String[] args) {
        int outsideVar = 5; // 'outsideVar' is declared and initialized outside the block

        {
            int insideVar = 10; // 'insideVar' is declared and initialized inside the block
            System.out.println("Inside the block - outsideVar: " + outsideVar + ", insideVar: " + insideVar);

            outsideVar = 15; // 'outsideVar' can be updated inside the block
            System.out.println("Inside the block - updated outsideVar: " + outsideVar);
        }

        // Uncommenting the line below would result in a compilation error
        // System.out.println("Outside the block - insideVar: " + insideVar);

        outsideVar = 20; // 'outsideVar' can be updated outside the block
        System.out.println("Outside the block - final outsideVar: " + outsideVar);
    }
}

Loop scope: Variables declared inside the loop have loop scope.

public class LoopScopeExample {

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            // Variable 'loopVar' is declared inside the loop and has loop scope
            int loopVar = i;
            System.out.println("Inside the loop - loopVar: " + loopVar);
        }

        // Uncommenting the line below would result in a compilation error
        // System.out.println("Outside the loop - loopVar: " + loopVar);
    }
}

Shadowing

Shadowing in Java is the practice of using variables in overlapping scopes with the same name where the variable in low-level scope overrides the variable of high-level scope. Here the variable at the high-level scope is shadowed by the low-level scope variable.

public class ShadowingExample {

    // Outer variable
    private static int x = 5;

    public static void main(String[] args) {
        // Outer variable 'x' is accessible here
        System.out.println("Outer variable x: " + x);

        // Declaring a variable with the same name 'x' within this block
        int x = 10;

        // Inner variable 'x' shadows the outer variable within this block
        System.out.println("Inner variable x: " + x);

        // You can still access the outer variable using the class name
        System.out.println("Outer variable x (using class name): " + ShadowingExample.x);

        // Calling a method with a parameter named 'x'
        printX(15);
    }

    // Method with a parameter named 'x'
    static void printX(int x) {
        // The parameter 'x' shadows the outer variable within this method
        System.out.println("Parameter x: " + x);
    }
}

Variable Arguments

Variable Arguments is used to take a variable number of arguments. A method that takes a variable number of arguments is a varargs method.

First, let’s look at the example without using varargs:

class NoVararg {

    public int sumNumber(int a, int b){
        return a+b;
    }

    public int sumNumber(int a, int b, int c){
        return a+b+c;
    }

    public static void main( String[] args ) {
        NoVararg object = new NoVararg();
        System.out.println(object.sumNumber(1, 2));
        System.out.println(object.sumNumber(1, 2, 3));
    }
}
// The ouput will be 3
                     6

As you can see, we have to overload sumNumber() method to make it work for 3 arguments.

What if the user wants to add 5 numbers or 10 or 100?

This can be handled neatly with the use of varargs. Let’s see a code example:

class VarargExample {

    public int sumNumber(int ... args){
        System.out.println("argument length: " + args.length);
        int sum = 0;
        for(int x: args){
            sum += x;
        }
        return sum;
    }

    public static void main( String[] args ) {
        VarargExample ex = new VarargExample();

        int sum2 = ex.sumNumber(2, 4);
        System.out.println("sum2 = " + sum2);

        int sum3 = ex.sumNumber(1, 3, 5);
        System.out.println("sum3 = " + sum3);

        int sum4 = ex.sumNumber(1, 3, 5, 7);
        System.out.println("sum4 = " + sum4);
    }
}
//The output will be
argument length: 2
sum2 = 6
argument length: 3
sum3 = 9
argument length: 4
sum4 = 16

Here, the sumNumber() method returns the sum of int parameters passed to it (doesn't matter the number of arguments passed).

Method overloading

Method overloading happens when two functions have the same name.

public class MyClass {

    // Compilation Error: Duplicate method with the same signature
    public void myMethod(int x, int y) {
        // Some code
    }

    // Error: Duplicate method with the same signature
    public void myMethod(int x, int y) {
        // Some other code
    }

    public static void main(String[] args) {
        // Code
    }
}

In this example, attempting to compile this code will result in a compilation error because both myMethod methods have the same name and the same parameter types.

If you want to have multiple methods with the same name, they must differ in either the number or types of their parameters. This concept is known as method overloading.

public class MethodOverloading {

    public int add(int a,int b) {
        return a + b;
    }

    public int add(int a,int b,int c) {
        return a + b + c;
    }

This is allowed to have different arguments with the same method name. At compile time, it decides which function to run.

Thanks for reading:)

You can connect with me on LinkedIn and Twitter.