Lecture from: 04.10.2024 | Video: Videos ETHZ
Functions
Think of functions as self-contained units of code designed to perform specific tasks. They’re like mini-programs within your larger program, encapsulating logic and making your code more organized, readable, and reusable.
Why Use Functions?
- Modularity: Break down complex problems into smaller, manageable chunks, improving clarity and reducing cognitive load.
- Reusability: Write code once and use it multiple times throughout your program, saving time and effort.
- Readability: Well-named functions clearly convey their purpose, making your code easier to understand and collaborate on.
- Abstraction: Hide implementation details behind a function interface, allowing you to focus on the “what” rather than the “how.”
Function Anatomy
Let’s dissect this example:
public
: Makes the function accessible from any class in your project.static
: Belongs to the class itself, not a specific instance of the class. You call static functions using the class name.int
: Specifies the return type – the data type of the value the function will produce.calculateArea
: The function’s name, chosen to clearly describe its purpose.(int length, int width)
: The parameter list – the input values the function expects. Each parameter has a name (length
,width
) and a data type (int
).return length * width;
: The function body – the code that performs the calculation and returns the result.
Calling Functions
Types and Parameters
Java uses value semantics for primitive types (int, double, boolean, etc.). When passing a primitive value to a function, a copy is made. Modifications inside the function only affect that copy; the original variable remains unchanged.
However, for non-primitive types (objects), Java uses reference semantics. A reference (memory address) to the object is passed, and any changes within the function directly modify the original object.
More on this later: Understanding How Java Passes Data Value Semantics vs. Reference Semantics
Important Note: Casting between primitive types can be done implicitly in some cases, but remember that casting from a wider type (e.g., double) to a narrower type (e.g., int) may result in errors or data loss due to truncation.
Advanced Concepts
FYI: Not part of this lecture yet…
- Variable Arguments (Varargs): Allow a function to accept a variable number of arguments of a specific type. This is denoted by adding an ellipsis (…) after the parameter list in the function definition.
- Keyword Arguments: Allow you to specify arguments by name, regardless of their order.
Understanding How Java Passes Data: Value Semantics vs. Reference Semantics
FYI: Not part of this lecture yet… - at least not in this detail
In object-oriented programming, functions (or methods) are reusable blocks of code that work with data. A crucial concept is how this data travels between the part of your program calling a function and the function itself.
Java uses two distinct mechanisms for passing data: value semantics and reference semantics.
Value Semantics: Copying the Essentials
Primitive data types in Java, like integers (int
), decimals (double
), true/false values (boolean
), and characters (char
), are handled using value semantics. When you pass a primitive value to a function, Java doesn’t simply send a pointer; it creates a complete copy of that value.
Think of it like making photocopies: each function receives its own independent copy, unaffected by changes made in other copies.
Example:
Key takeaway: Any modifications made to num
within the modifyNumber
function only affect that specific copy. The original number
variable remains untouched at 10.
Reference Semantics: Sharing the Address
Non-primitive data types, like objects (created using classes), behave differently. Java passes a reference to these objects—essentially a memory address pointing to their location. When you modify an object through this reference inside a function, the changes are reflected in the original object outside the function.
Example:
Key takeaway: The changeName
function directly manipulates the Person
object referenced by person
. This means that calling changeName
permanently alters the name
of the person instance in the main
method.
Why Java Does It This Way
Java’s approach to data passing helps maintain program integrity and predictability:
- Value Semantics (Primitives): Protects data from accidental modification within functions. Changes made are isolated to copies, preserving original values.
- Reference Semantics (Objects): Allows for efficient sharing and modification of complex data structures. Functions can directly work on objects, simplifying code and promoting object-oriented principles.
Variable Scope: Where Your Data Resides
In programming, scope defines the accessibility of variables within different parts of your code. Think of it as layers of visibility – some variables are only visible within a specific function or block, while others have a wider reach.
Types of Scope
- Local Scope: Variables declared inside a function or block (e.g., within an
if
statement or loop) are local to that function or block. They can only be accessed from within that specific region.
- Instance Scope: Variables declared inside a class but outside any function are instance variables. They belong to each individual object of that class and can be accessed by all functions within the class.
- Static Scope: Static variables are declared using the
static
keyword and belong to the class itself, not individual instances. They can be accessed directly using the class name (e.g.,MyClass.myStaticVariable
) and are shared among all objects of that class.
- Block Scope (Introduced in Java 14): Variables declared within a block of code (e.g., inside an
if
statement,for
loop, or switch statement) have block scope and are only accessible within that specific block. A block is denoted by{ //... }
Scope is crucial for:
- Preventing Name Collisions: Different parts of your code can use variables with the same name without interfering with each other if they’re in different scopes.
- Data Encapsulation: Local variables keep data localized, reducing unintended modifications and improving code organization.
- Controlling Visibility: You can deliberately choose which variables are accessible from where to enforce security and maintain code clarity.
Continue here: 06 Java (Functions, Loops, Side Effects, Do-While)