Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Object-Oriented Programming (OOP)  Module 1 – Objects and classes

References, identity and aliasing

Why two variables can name one object, what b = a copies in 14 languages, how arguments are passed, and when to test identity instead of equality.

  • Beginner
  • 15 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, Java temurin-25.0.4+101.0.LTS and quickjs 0.32.0
  • By MySmartCoPilot
Free lesson, Pro answers: 15 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Predict when two variables share one object and when assignment copies it
  • Explain why a function that changes its argument is seen by the caller, while rebinding the argument is not
  • Choose an identity test or an equality test in each language
  • Avoid the cached-integer traps of Java's == and Python's is

Before you start

What you’ll learn · Before you start

What you will learn

  • Predict when two variables share one object and when assignment copies it
  • Explain why a function that changes its argument is seen by the caller, while rebinding the argument is not
  • Choose an identity test or an equality test in each language
  • Avoid the cached-integer traps of Java's == and Python's is

Before you start

On this page

A card holds a balance of 100. The program runs b = a, then b.balance -= 30, and prints a.balance. In Python, Java and JavaScript it prints 70; in C++, Go and Swift it prints 100. Both answers are correct, because b = a means different things: either a second name for the same object, or a second object.

Below, that line runs in 14 languages. After it come function arguments, the difference between asking “the same object?” and “equal values?”, and the two caches that make an identity test give the answer an equality test should.

A variable can hold a reference instead of the object

In Python, Java, JavaScript and most languages of this track, a variable that names an object does not contain the object. It holds a reference: A value that leads to an object stored elsewhere. In Python, Java and JavaScript a variable of an object type holds a reference, so copying the variable copies the reference, not the object., a way to reach an object that lives somewhere else. Python’s FAQ says it plainly: “Variables are simply names that refer to objects.” The Java Language Specification says the same in its own terms: reference values “are pointers to these objects”. Step through five lines and watch the arrows:

Interactive · step by step Names on the left, objects on the right Step through five lines of Python. Each arrow is a reference from a name to an object; watch how many objects there are.
Names on the left, objects on the right, step 5 of 5. Showing a, b, c, Card object (balance 70), Card object. In focus: Card object, Card object.abcCard objectbalance 70Card objectbalance 70equal balance, different object

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. a = Card(100). Card(100) makes an object, and a = names it. The name is not the object. a holds a reference, drawn as an arrow from the name to the card.
  2. b = a. b = a copies the reference, not the card. Two names now point at one object, so b is an alias of a.
  3. b.balance -= 30. The change goes through b to the only card there is. Reading a.balance now gives 70 as well, although no line mentioned a.
  4. c = Card(70). Card(70) makes a second object with the same balance, and c names it. Two cards, three names.
  5. Same object, or equal?. a is b is True, because both arrows end at one card. a is c is False, although a.balance == c.balance is True. Identity asks about the arrows, equality about what they point at.

After b = a there is still only one card, reached by two names: b is an alias: A second name for the same object. After b = a in Python or Java, b is an alias of a, and a change made through either name is seen through both. of a. Changing the card through b changes what a sees, because there is nothing else for a to see. The new Card(70) made a second card with an equal balance, which shares nothing with the first.

A reference can also lead to no object at all: null in Java, JavaScript and C#, nil in Go. Python’s None and Ruby’s nil play the same part, although each is a single object of its own that stands for “no value”. Calling a method that only a card has, such as pay, then fails while the program runs, with AttributeError, NullPointerException or TypeError. Rust has no null reference at all: a value that may be missing has the type Option. Swift’s optionals and Kotlin’s nullable types, such as String?, do the same job, and the compiler makes you check them first.

Checkpoint

Check: Variables that hold references

  1. Question 1 of 2 Python runs a = Card(100); b = a; c = Card(70). How many Card objects exist?

    Type a number.

    A hint

    Count the calls of Card(...), not the names.

    Show the answer: Question 1 of 2

    Answer: 2 objects

    Only Card(100) and Card(70) make objects. b = a made a second name for the first card, so three names reach two cards.

  2. Question 2 of 2 In Python, a = Card(100), b = a and then b.balance -= 30. What is a.balance?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: 70, because a and b reach the same card

    Python's FAQ: y = x does not create a copy, it creates a new name for the object x refers to. a.balance is 70, as the Python tab of the lesson's assignment program printed.

Assignment shares a reference or copies a value

Whether b = a gives the object a second name or makes a second object depends on the language and on the kind of type. The program below writes a card in each language’s most natural way and runs b = a followed by a change through b. This time the languages do not agree, and the block says why for each one:

One program · 14 languages What b = a does to a card

Why the outputs differ: The same line b = a gives the card a second name in the languages whose card is a class, and copies it in those whose card is a struct, so a change through b reaches a only in the first group.

Python 3.14 · main.py

class Card:
    def __init__(self, balance: int) -> None:
        self.balance = balance


a = Card(100)
b = a  # copy the card, or give it a second name?
b.balance -= 30
print(f"a.balance = {a.balance}, b.balance = {b.balance}")

In Python: b = a never copies in Python. It binds a second name to the object that a names, whatever its type.

What Python printed

a.balance = 70, b.balance = 70

Recorded with CPython 3.14.8 on macOS 26 arm64. It runs in your browser with Pyodide 314.0.7 (CPython 3.14.2). To run it yourself: python3 main.py

JavaScript Node 24 · main.mjs

class Card {
  constructor(balance) {
    this.balance = balance;
  }
}

const a = new Card(100);
const b = a; // copy the card, or give it a second name?
b.balance -= 30;
console.log(`a.balance = ${a.balance}, b.balance = ${b.balance}`);

What JavaScript printed

a.balance = 70, b.balance = 70

Recorded with Node.js 24.21.0 LTS (V8 13.6) on macOS 26 arm64. It runs in your browser with QuickJS (quickjs-emscripten 0.32.0). To run it yourself: node main.mjs

Java 25 LTS · Main.java

public class Main {
  public static void main(String[] args) {
    Card a = new Card(100);
    Card b = a; // copy the card, or give it a second name?
    b.balance -= 30;
    System.out.println("a.balance = " + a.balance + ", b.balance = " + b.balance);
  }
}

final class Card {
  int balance;

  Card(int balance) {
    this.balance = balance;
  }
}

In Java: b = a copies the reference, not the object. Java never copies an object unless code asks for it.

What Java printed

a.balance = 70, b.balance = 70

Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. It runs in your browser with a TeaVM 0.16.0 build in QuickJS (quickjs-emscripten 0.32.0). To run it yourself: java Main.java

C++23 · main.cpp

#include <print>

struct Card {
  int balance;
};

int main() {
  Card a{100};
  Card b = a; // copy the card, or give it a second name?
  b.balance -= 30;
  std::println("a.balance = {}, b.balance = {}", a.balance, b.balance);
}

Why C++ prints something else: A C++ object is a value, so Card b = a copies every field and a stays at 100. Card& b = a would make b a second name.

What C++ printed

a.balance = 100, b.balance = 70

Recorded with Apple clang 21.0.0 (C++23) and GCC 16.2.0 on macOS 26 arm64. It runs in your browser with Clang in WebAssembly (YoWASP) 22.0.0-git20542-10. To run it yourself: clang++ -std=c++23 -O2 -Wall -Wextra -Werror -pedantic -fsanitize=address,undefined main.cpp -o main && ./main

C# 14 · .NET 10 · app.cs

var a = new Card(100);
var b = a; // copy the card, or give it a second name?
b.Balance -= 30;
Console.WriteLine($"a.balance = {a.Balance}, b.balance = {b.Balance}");

sealed class Card(int balance)
{
    public int Balance { get; set; } = balance;
}

In C#: Card is a class, a reference type, so b = a shares it. Declared as a struct, Card would be copied, as in Swift.

What C# printed

a.balance = 70, b.balance = 70

Recorded with C# 14 on .NET 10.0.12 (SDK 10.0.401) on macOS 26 arm64. It runs in your browser with the .NET runtime for WebAssembly 10.0.12. To run it yourself: dotnet run app.cs

C23 · main.c

#include <stdio.h>

struct card {
  int balance;
};

int main(void) {
  struct card a = {.balance = 100};
  struct card b = a; // copy the card, or give it a second name?
  b.balance -= 30;
  printf("a.balance = %d, b.balance = %d\n", a.balance, b.balance);
  return 0;
}

Why C prints something else: Assigning a struct copies all of its members, so b is a second card. A pointer, struct card *b = &a, would share it.

What C printed

a.balance = 100, b.balance = 70

Recorded with Apple clang 21.0.0 (C23) and GCC 16.2.0 on macOS 26 arm64. It runs in your browser with Clang in WebAssembly (YoWASP) 22.0.0-git20542-10. To run it yourself: clang -std=c23 -O2 -Wall -Wextra -Werror -pedantic -fsanitize=address,undefined main.c -o main && ./main

PHP 8.5 · main.php

<?php

declare(strict_types=1);

final class Card
{
    public function __construct(public int $balance) {}
}

$a = new Card(100);
$b = $a; // copy the card, or give it a second name?
$b->balance -= 30;
echo "a.balance = {$a->balance}, b.balance = {$b->balance}\n";

In PHP: An object variable holds an identifier of the object, and $b = $a copies the identifier. PHP arrays, unlike objects, are copied by assignment.

What PHP printed

a.balance = 70, b.balance = 70

Recorded with PHP 8.5.11 (CLI) on macOS 26 arm64. It runs in your browser with php-wasm 0.2.0 (PHP 8.5.2). To run it yourself: php main.php

Rust 1.99 · main.rs

#[derive(Clone, Copy)]
struct Card {
    balance: i32,
}

fn main() {
    let a = Card { balance: 100 };
    let mut b = a; // copy the card, or give it a second name?
    b.balance -= 30;
    println!("a.balance = {}, b.balance = {}", a.balance, b.balance);
}

Why Rust prints something else: Card derives Copy, so let b = a copies it. Without Copy the line would move the card, and a could not be used again.

What Rust printed

a.balance = 100, b.balance = 70

Recorded with rustc 1.99.0 (edition 2024) on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: rustc --edition 2024 -O main.rs && ./main

TypeScript 6.0 · main.ts

class Card {
  balance: number;

  constructor(balance: number) {
    this.balance = balance;
  }
}

const a = new Card(100);
const b = a; // copy the card, or give it a second name?
b.balance -= 30;
console.log(`a.balance = ${a.balance}, b.balance = ${b.balance}`);

What TypeScript printed

a.balance = 70, b.balance = 70

Recorded with TypeScript 6.0.3 (strict), run by Node.js 24.21.0 on macOS 26 arm64. It runs in your browser with QuickJS (quickjs-emscripten 0.32.0) after Sucrase 3.35.1. To run it yourself: npx -p typescript@6.0.3 tsc --noEmit --strict main.ts && node main.ts

Go 1.27 · main.go

package main

import "fmt"

type Card struct {
	balance int
}

func main() {
	a := Card{balance: 100}
	b := a // copy the card, or give it a second name?
	b.balance -= 30
	fmt.Printf("a.balance = %d, b.balance = %d\n", a.balance, b.balance)
}

Why Go prints something else: Assigning a struct value copies its fields, so b is a separate card. b := &a would share it through a pointer.

What Go printed

a.balance = 100, b.balance = 70

Recorded with Go 1.27.1 on macOS 26 arm64. It runs in your browser with the Go compiler in WebAssembly 1.27.1. To run it yourself: go run main.go

Swift 6.4 · main.swift

struct Card {
  var balance: Int
}

let a = Card(balance: 100)
var b = a  // copy the card, or give it a second name?
b.balance -= 30
print("a.balance = \(a.balance), b.balance = \(b.balance)")

Why Swift prints something else: Card is a struct, a value type, so var b = a copies it. Declared as a final class, it would be shared.

What Swift printed

a.balance = 100, b.balance = 70

Recorded with Apple Swift 6.4 on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: swiftc -O main.swift -o main && ./main

Ruby 4.0 · main.rb

# frozen_string_literal: true

# A card that holds a balance.
class Card
  attr_accessor :balance

  def initialize(balance)
    @balance = balance
  end
end

a = Card.new(100)
b = a # copy the card, or give it a second name?
b.balance -= 30
puts "a.balance = #{a.balance}, b.balance = #{b.balance}"

What Ruby printed

a.balance = 70, b.balance = 70

Recorded with CRuby 4.0.7 on macOS 26 arm64. It runs in your browser with ruby.wasm 2.10.1 (Ruby 4.0.0). To run it yourself: ruby main.rb

Kotlin 2.4 · Main.kt

class Card(
    var balance: Int,
)

fun main() {
    val a = Card(100)
    val b = a // copy the card, or give it a second name?
    b.balance -= 30
    println("a.balance = ${a.balance}, b.balance = ${b.balance}")
}

In Kotlin: b = a copies the reference. Even a data class, which gives copy(), is shared until copy() is called.

What Kotlin printed

a.balance = 70, b.balance = 70

Recorded with Kotlin 2.4.20 on Java 25.0.4.1 on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: kotlinc Main.kt -include-runtime -d main.jar && java -jar main.jar

Dart 3.13 · main.dart

class Card {
  Card(this.balance);

  int balance;
}

void main() {
  final a = Card(100);
  final b = a; // copy the card, or give it a second name?
  b.balance -= 30;
  print('a.balance = ${a.balance}, b.balance = ${b.balance}');
}

What Dart printed

a.balance = 70, b.balance = 70

Recorded with Dart 3.13.5 on macOS 26 arm64. It runs in your browser with a WebAssembly build by dart2wasm 3.13.5. To run it yourself: dart run main.dart

Nine languages printed 70 for a, and five printed 100. In the nine, the card is a class, and a variable of a class type holds a reference, so b = a copied the reference. In C, C++, Go and Swift the card is a struct, and assigning a struct copies all of its fields; Rust’s card copies too, but only because it was declared Copy. Without that, Rust moves the value: after let b = a;, the compiler refuses any later use of a. The table sums up the rule of each language and how to get the other behaviour:

Language b = a for its usual record type How to get the other behaviour
Python, Ruby, JavaScript, TypeScript, PHP, Dart Shares: every object variable is a reference Copy explicitly: copy.copy(a), a.dup, clone $a, or a copy method
Java, Kotlin Shares objects; Java copies primitives such as int A copy constructor, or copy() of a Kotlin data class
C# Shares a class; copies a struct Choose struct or class
Swift Shares a class; copies a struct Choose struct or class
C++, C, Go Copies the whole value Use a reference or a pointer: Card& b = a, &a
Rust Moves the value, or copies a Copy type Borrow it: let b = &mut a

A backup that is not a backup

backup = marks before changing marks keeps nothing in Python, Java or JavaScript: both names reach the same list, so the “backup” changes with it. Copy the list itself: marks.copy() in Python, new ArrayList<>(marks) in Java, [...marks] in JavaScript.

Checkpoint

Check: What assignment shares or copies

  1. Question 1 of 2 The C++ version of the program ran Card b = a; b.balance -= 30. What did it print for a.balance, and why?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: 100, because a C++ object is a value and Card b = a copied the whole card

    C, C++, Go and Swift copied the card, and Rust did too because its card is Copy. The nine languages whose card is a class shared it and printed 70.

  2. Question 2 of 2 A Python program does backup = marks, then marks.append(64). What does backup hold?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The list with 64 in it, because backup and marks are two names for one list

    backup = marks gave the list a second name. To keep the old contents, copy the list itself before changing it: marks.copy() in Python, new ArrayList<>(marks) in Java, [...marks] in JavaScript.

Arguments are passed by value, and the value may be a reference

A function receives its arguments the way assignment hands out values: Python’s FAQ says arguments “are passed by assignment”. For an object, what the function receives is a copy of the caller’s reference. Two things can then happen inside it, with opposite results for the caller. The function can change the object through its copy of the reference, which the caller sees, or it can point its own parameter at a different object, which the caller never sees:

One program · 14 languages Passing a card to a function, then testing identity

Python 3.14 · main.py

class Card:
    def __init__(self, balance: int) -> None:
        self.balance = balance


def charge_fee(card: Card) -> None:
    card.balance -= 10  # changes the object the caller passed in


def swap_card(card: Card) -> int:
    card = Card(card.balance + 400)  # points this function's own name at a new card
    return card.balance


mine = Card(100)
charge_fee(mine)
print(f"after charge_fee(mine): {mine.balance}")
inside = swap_card(mine)
print(f"swap_card(mine) made a card of {inside}; mine is still {mine.balance}")

alias = mine
twin = Card(mine.balance)
print(f"alias and mine: {'same object' if alias is mine else 'different objects'}")
print(f"twin and mine: {'same object' if twin is mine else 'different objects'}")
print(f"equal balances: {'yes' if twin.balance == mine.balance else 'no'}")

In Python: Python passes arguments by assignment. card is a new name bound to the caller's object, so card = Card(…) rebinds that name only, and is tests identity.

Recorded with CPython 3.14.8 on macOS 26 arm64. It runs in your browser with Pyodide 314.0.7 (CPython 3.14.2). To run it yourself: python3 main.py

JavaScript Node 24 · main.mjs

class Card {
  constructor(balance) {
    this.balance = balance;
  }
}

function chargeFee(card) {
  card.balance -= 10; // changes the object the caller passed in
}

function swapCard(card) {
  card = new Card(card.balance + 400); // points this function's own name at a new card
  return card.balance;
}

const mine = new Card(100);
chargeFee(mine);
console.log(`after charge_fee(mine): ${mine.balance}`);
const inside = swapCard(mine);
console.log(`swap_card(mine) made a card of ${inside}; mine is still ${mine.balance}`);

const alias = mine;
const twin = new Card(mine.balance);
console.log(`alias and mine: ${alias === mine ? "same object" : "different objects"}`);
console.log(`twin and mine: ${twin === mine ? "same object" : "different objects"}`);
console.log(`equal balances: ${twin.balance === mine.balance ? "yes" : "no"}`);

In JavaScript: === on two objects is true only when both sides refer to the same object, whatever their fields hold.

Recorded with Node.js 24.21.0 LTS (V8 13.6) on macOS 26 arm64. It runs in your browser with QuickJS (quickjs-emscripten 0.32.0). To run it yourself: node main.mjs

Java 25 LTS · Main.java

public class Main {
  static void chargeFee(Card card) {
    card.balance -= 10; // changes the object the caller passed in
  }

  static int swapCard(Card card) {
    card = new Card(card.balance + 400); // points this function's own name at a new card
    return card.balance;
  }

  public static void main(String[] args) {
    Card mine = new Card(100);
    chargeFee(mine);
    System.out.println("after charge_fee(mine): " + mine.balance);
    int inside = swapCard(mine);
    System.out.println(
        "swap_card(mine) made a card of " + inside + "; mine is still " + mine.balance);

    Card alias = mine;
    Card twin = new Card(mine.balance);
    System.out.println("alias and mine: " + (alias == mine ? "same object" : "different objects"));
    System.out.println("twin and mine: " + (twin == mine ? "same object" : "different objects"));
    System.out.println("equal balances: " + (twin.balance == mine.balance ? "yes" : "no"));
  }
}

final class Card {
  int balance;

  Card(int balance) {
    this.balance = balance;
  }
}

In Java: Java passes every argument by value, and for an object the value is a reference. card = new Card(…) changes the parameter, never the caller's variable.

Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. It runs in your browser with a TeaVM 0.16.0 build in QuickJS (quickjs-emscripten 0.32.0). To run it yourself: java Main.java

C++23 · main.cpp

#include <print>

struct Card {
  int balance;
};

void charge_fee(Card *card) {
  card->balance -= 10; // changes the object the caller passed in
}

int swap_card(const Card *card) {
  const Card fresh{card->balance + 400};
  card = &fresh; // points this function's own name at a new card
  return card->balance;
}

int main() {
  Card mine{100};
  charge_fee(&mine);
  std::println("after charge_fee(mine): {}", mine.balance);
  const int inside = swap_card(&mine);
  std::println("swap_card(mine) made a card of {}; mine is still {}", inside,
               mine.balance);

  const Card *alias = &mine;
  const Card twin{mine.balance};
  std::println("alias and mine: {}",
               alias == &mine ? "same object" : "different objects");
  std::println("twin and mine: {}",
               &twin == &mine ? "same object" : "different objects");
  std::println("equal balances: {}",
               twin.balance == mine.balance ? "yes" : "no");
}

In C++: The functions take pointers, which is what Java and Python pass. A reference parameter, Card& card, would be a true alias, and assigning to it would overwrite mine.

Recorded with Apple clang 21.0.0 (C++23) and GCC 16.2.0 on macOS 26 arm64. It runs in your browser with Clang in WebAssembly (YoWASP) 22.0.0-git20542-10. To run it yourself: clang++ -std=c++23 -O2 -Wall -Wextra -Werror -pedantic -fsanitize=address,undefined main.cpp -o main && ./main

C# 14 · .NET 10 · app.cs

var mine = new Card(100);
ChargeFee(mine);
Console.WriteLine($"after charge_fee(mine): {mine.Balance}");
int inside = SwapCard(mine);
Console.WriteLine($"swap_card(mine) made a card of {inside}; mine is still {mine.Balance}");

var alias = mine;
var twin = new Card(mine.Balance);
Console.WriteLine($"alias and mine: {Sameness(ReferenceEquals(alias, mine))}");
Console.WriteLine($"twin and mine: {Sameness(ReferenceEquals(twin, mine))}");
Console.WriteLine($"equal balances: {(twin.Balance == mine.Balance ? "yes" : "no")}");

static void ChargeFee(Card card)
{
    card.Balance -= 10; // changes the object the caller passed in
}

static string Sameness(bool same) => same ? "same object" : "different objects";

static int SwapCard(Card card)
{
    card = new Card(card.Balance + 400); // points this function's own name at a new card
    return card.Balance;
}

sealed class Card(int balance)
{
    public int Balance { get; set; } = balance;
}

In C#: The parameter holds a copy of the reference. Declared ref Card card, it would be the caller's variable itself, and card = new Card(…) would replace mine.

Recorded with C# 14 on .NET 10.0.12 (SDK 10.0.401) on macOS 26 arm64. It runs in your browser with the .NET runtime for WebAssembly 10.0.12. To run it yourself: dotnet run app.cs

C23 · main.c

#include <stdio.h>

struct card {
  int balance;
};

static void charge_fee(struct card *card) {
  card->balance -= 10; // changes the object the caller passed in
}

static int swap_card(const struct card *card) {
  const struct card fresh = {.balance = card->balance + 400};
  card = &fresh; // points this function's own name at a new card
  return card->balance;
}

int main(void) {
  struct card mine = {.balance = 100};
  charge_fee(&mine);
  printf("after charge_fee(mine): %d\n", mine.balance);
  const int inside = swap_card(&mine);
  printf("swap_card(mine) made a card of %d; mine is still %d\n", inside,
         mine.balance);

  const struct card *alias = &mine;
  const struct card twin = {.balance = mine.balance};
  printf("alias and mine: %s\n",
         alias == &mine ? "same object" : "different objects");
  printf("twin and mine: %s\n",
         &twin == &mine ? "same object" : "different objects");
  printf("equal balances: %s\n", twin.balance == mine.balance ? "yes" : "no");
  return 0;
}

In C: C passes the pointer itself by value, so card = &fresh changes only the copy inside swap_card, and comparing pointers is the identity test.

Recorded with Apple clang 21.0.0 (C23) and GCC 16.2.0 on macOS 26 arm64. It runs in your browser with Clang in WebAssembly (YoWASP) 22.0.0-git20542-10. To run it yourself: clang -std=c23 -O2 -Wall -Wextra -Werror -pedantic -fsanitize=address,undefined main.c -o main && ./main

PHP 8.5 · main.php

<?php

declare(strict_types=1);

final class Card
{
    public function __construct(public int $balance) {}
}

function chargeFee(Card $card): void
{
    $card->balance -= 10; // changes the object the caller passed in
}

function swapCard(Card $card): int
{
    $card = new Card($card->balance + 400); // points this function's own name at a new card

    return $card->balance;
}

$mine = new Card(100);
chargeFee($mine);
echo "after charge_fee(mine): {$mine->balance}\n";
$inside = swapCard($mine);
echo "swap_card(mine) made a card of {$inside}; mine is still {$mine->balance}\n";

$alias = $mine;
$twin = new Card($mine->balance);
echo 'alias and mine: ' . ($alias === $mine ? 'same object' : 'different objects') . "\n";
echo 'twin and mine: ' . ($twin === $mine ? 'same object' : 'different objects') . "\n";
echo 'equal balances: ' . ($twin->balance === $mine->balance ? 'yes' : 'no') . "\n";

In PHP: The parameter receives a copy of the object's identifier. PHP's &$card would pass the caller's variable itself, like a C++ reference.

Recorded with PHP 8.5.11 (CLI) on macOS 26 arm64. It runs in your browser with php-wasm 0.2.0 (PHP 8.5.2). To run it yourself: php main.php

Rust 1.99 · main.rs

struct Card {
    balance: i32,
}

fn charge_fee(card: &mut Card) {
    card.balance -= 10; // changes the object the caller passed in
}

fn swap_card(card: &Card) -> i32 {
    let fresh = Card {
        balance: card.balance + 400,
    };
    let card = &fresh; // points this function's own name at a new card
    card.balance
}

fn main() {
    let mut mine = Card { balance: 100 };
    charge_fee(&mut mine);
    println!("after charge_fee(mine): {}", mine.balance);
    let inside = swap_card(&mine);
    println!(
        "swap_card(mine) made a card of {inside}; mine is still {}",
        mine.balance
    );

    let alias = &mine;
    let twin = Card {
        balance: mine.balance,
    };
    let same = |x: &Card| {
        if std::ptr::eq(x, &mine) {
            "same object"
        } else {
            "different objects"
        }
    };
    println!("alias and mine: {}", same(alias));
    println!("twin and mine: {}", same(&twin));
    let equal = if twin.balance == mine.balance {
        "yes"
    } else {
        "no"
    };
    println!("equal balances: {equal}");
}

In Rust: A borrowed parameter cannot point at a value made inside the function, so swap_card shadows card with a new binding to fresh. std::ptr::eq compares addresses.

Recorded with rustc 1.99.0 (edition 2024) on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: rustc --edition 2024 -O main.rs && ./main

TypeScript 6.0 · main.ts

class Card {
  balance: number;

  constructor(balance: number) {
    this.balance = balance;
  }
}

function chargeFee(card: Card): void {
  card.balance -= 10; // changes the object the caller passed in
}

function swapCard(card: Card): number {
  card = new Card(card.balance + 400); // points this function's own name at a new card
  return card.balance;
}

const mine = new Card(100);
chargeFee(mine);
console.log(`after charge_fee(mine): ${mine.balance}`);
const inside = swapCard(mine);
console.log(`swap_card(mine) made a card of ${inside}; mine is still ${mine.balance}`);

const alias = mine;
const twin = new Card(mine.balance);
console.log(`alias and mine: ${alias === mine ? "same object" : "different objects"}`);
console.log(`twin and mine: ${twin === mine ? "same object" : "different objects"}`);
console.log(`equal balances: ${twin.balance === mine.balance ? "yes" : "no"}`);

Recorded with TypeScript 6.0.3 (strict), run by Node.js 24.21.0 on macOS 26 arm64. It runs in your browser with QuickJS (quickjs-emscripten 0.32.0) after Sucrase 3.35.1. To run it yourself: npx -p typescript@6.0.3 tsc --noEmit --strict main.ts && node main.ts

Go 1.27 · main.go

package main

import "fmt"

type Card struct {
	balance int
}

func chargeFee(card *Card) {
	card.balance -= 10 // changes the object the caller passed in
}

func swapCard(card *Card) int {
	card = &Card{balance: card.balance + 400} // points this function's own name at a new card
	return card.balance
}

func sameOrNot(same bool) string {
	if same {
		return "same object"
	}
	return "different objects"
}

func main() {
	mine := &Card{balance: 100}
	chargeFee(mine)
	fmt.Printf("after charge_fee(mine): %d\n", mine.balance)
	inside := swapCard(mine)
	fmt.Printf("swap_card(mine) made a card of %d; mine is still %d\n", inside, mine.balance)

	alias := mine
	twin := &Card{balance: mine.balance}
	fmt.Printf("alias and mine: %s\n", sameOrNot(alias == mine))
	fmt.Printf("twin and mine: %s\n", sameOrNot(twin == mine))
	equal := "no"
	if twin.balance == mine.balance {
		equal = "yes"
	}
	fmt.Printf("equal balances: %s\n", equal)
}

In Go: Go passes everything by value, so card is a copy of the caller's pointer and pointing it at a new Card leaves mine alone. == on pointers tests identity.

Recorded with Go 1.27.1 on macOS 26 arm64. It runs in your browser with the Go compiler in WebAssembly 1.27.1. To run it yourself: go run main.go

Swift 6.4 · main.swift

final class Card {
  var balance: Int

  init(balance: Int) {
    self.balance = balance
  }
}

func chargeFee(_ card: Card) {
  card.balance -= 10  // changes the object the caller passed in
}

func swapCard(_ card: Card) -> Int {
  var card = card
  card = Card(balance: card.balance + 400)  // points this function's own name at a new card
  return card.balance
}

let mine = Card(balance: 100)
chargeFee(mine)
print("after charge_fee(mine): \(mine.balance)")
let inside = swapCard(mine)
print("swap_card(mine) made a card of \(inside); mine is still \(mine.balance)")

let alias = mine
let twin = Card(balance: mine.balance)
print("alias and mine: \(alias === mine ? "same object" : "different objects")")
print("twin and mine: \(twin === mine ? "same object" : "different objects")")
print("equal balances: \(twin.balance == mine.balance ? "yes" : "no")")

In Swift: Card is a class here, so it is shared. Parameters are constants, so swapCard copies the reference into var card before pointing it elsewhere; === tests identity.

Recorded with Apple Swift 6.4 on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: swiftc -O main.swift -o main && ./main

Ruby 4.0 · main.rb

# frozen_string_literal: true

# A card that holds a balance.
class Card
  attr_accessor :balance

  def initialize(balance)
    @balance = balance
  end
end

def charge_fee(card)
  card.balance -= 10 # changes the object the caller passed in
end

def swap_card(card)
  card = Card.new(card.balance + 400) # points this function's own name at a new card
  card.balance
end

mine = Card.new(100)
charge_fee(mine)
puts "after charge_fee(mine): #{mine.balance}"
inside = swap_card(mine)
puts "swap_card(mine) made a card of #{inside}; mine is still #{mine.balance}"

alias_name = mine
twin = Card.new(mine.balance)
puts "alias and mine: #{alias_name.equal?(mine) ? 'same object' : 'different objects'}"
puts "twin and mine: #{twin.equal?(mine) ? 'same object' : 'different objects'}"
puts "equal balances: #{twin.balance == mine.balance ? 'yes' : 'no'}"

In Ruby: Ruby passes references by value, as Python does. alias is a keyword in Ruby, so the second name is alias_name, and equal? tests identity.

Recorded with CRuby 4.0.7 on macOS 26 arm64. It runs in your browser with ruby.wasm 2.10.1 (Ruby 4.0.0). To run it yourself: ruby main.rb

Kotlin 2.4 · Main.kt

class Card(
    var balance: Int,
)

fun chargeFee(card: Card) {
    card.balance -= 10 // changes the object the caller passed in
}

fun swapCard(card: Card): Int {
    var current = card
    current = Card(current.balance + 400) // points this function's own name at a new card
    return current.balance
}

fun main() {
    val mine = Card(100)
    chargeFee(mine)
    println("after charge_fee(mine): ${mine.balance}")
    val inside = swapCard(mine)
    println("swap_card(mine) made a card of $inside; mine is still ${mine.balance}")

    val alias = mine
    val twin = Card(mine.balance)
    println("alias and mine: ${if (alias === mine) "same object" else "different objects"}")
    println("twin and mine: ${if (twin === mine) "same object" else "different objects"}")
    println("equal balances: ${if (twin.balance == mine.balance) "yes" else "no"}")
}

In Kotlin: Parameters are read-only, so swapCard copies the reference into var current before pointing it at a new card. === tests identity and == equality.

Recorded with Kotlin 2.4.20 on Java 25.0.4.1 on macOS 26 arm64. It runs in your browser with a WebAssembly build on the WASI runner (browser_wasi_shim 0.4.2). To run it yourself: kotlinc Main.kt -include-runtime -d main.jar && java -jar main.jar

Dart 3.13 · main.dart

class Card {
  Card(this.balance);

  int balance;
}

void chargeFee(Card card) {
  card.balance -= 10; // changes the object the caller passed in
}

int swapCard(Card card) {
  card = Card(
    card.balance + 400,
  ); // points this function's own name at a new card
  return card.balance;
}

void main() {
  final mine = Card(100);
  chargeFee(mine);
  print('after charge_fee(mine): ${mine.balance}');
  final inside = swapCard(mine);
  print(
    'swap_card(mine) made a card of $inside; mine is still ${mine.balance}',
  );

  final alias = mine;
  final twin = Card(mine.balance);
  print(
    'alias and mine: ${identical(alias, mine) ? 'same object' : 'different objects'}',
  );
  print(
    'twin and mine: ${identical(twin, mine) ? 'same object' : 'different objects'}',
  );
  print('equal balances: ${twin.balance == mine.balance ? 'yes' : 'no'}');
}

In Dart: Reassigning a parameter changes only the function's own variable. identical() tests whether two references point to one object.

Recorded with Dart 3.13.5 on macOS 26 arm64. It runs in your browser with a WebAssembly build by dart2wasm 3.13.5. To run it yourself: dart run main.dart

What every language printed

after charge_fee(mine): 90
swap_card(mine) made a card of 490; mine is still 90
alias and mine: same object
twin and mine: different objects
equal balances: yes

charge_fee took 10 off the card the caller passed, and the caller saw 90. swap_card pointed its parameter at a new card of 490 and returned that balance, but mine was still 90: the function only ever had a copy of the reference. Java’s tutorial puts the rule the same way: object arguments “are also passed into methods by value”, so the passed-in reference “still references the same object as before”. The PHP manual adds that object variables are not aliases but “hold a copy of the identifier, which points to the same object”.

Some languages also offer true pass-by-reference, in which the function receives the caller’s variable itself: Card& card in C++, ref Card card in C#, &$card in PHP. Then card = … inside the function replaces the caller’s card, exactly what swap_card could not do. The notes under the C++, C# and PHP tabs show where that would change the output.

Key idea

A function receives a copy of the caller’s reference: changing the object through it is seen by the caller, while pointing the parameter at another object is not.

Checkpoint

Check: How arguments are passed

  1. Question 1 of 2 A Java method is void reset(Card card) { card = new Card(0); }. After reset(mine), what is mine.balance if it was 90?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: 90, because the method changed only its own copy of the reference

    dev.java: object arguments are passed by value, so the passed-in reference still references the same object. To change the caller's card, change its fields, as charge_fee did, or return the new card.

  2. Question 2 of 2 def fill(items) does items.append(1) and then items = [9]. After nums = [] and fill(nums), what is nums?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: [1], because append changed the caller's list and items = [9] only rebound the parameter

    The same two moves as charge_fee and swap_card: changing the object through the parameter is seen by the caller, rebinding the parameter is not.

Same object or equal values: two different questions

The last three lines of the program asked two questions about mine, alias and twin. Is it the same object? Do the two hold equal values? alias and mine are one object; twin is a different object whose balance happens to be equal. Every language has a way to ask each question:

Language Same object? Equal values?
Python a is b a == b
Java a == b a.equals(b)
JavaScript, TypeScript a === b no built-in test; compare the fields
C# ReferenceEquals(a, b) a.Equals(b), == when the type defines it
C++, C &a == &b, or two pointers with == a == b when operator== is defined (C++)
Go p == q on two pointers a == b on two struct values
Rust std::ptr::eq(a, b) a == b with PartialEq
Swift, Kotlin a === b a == b
Ruby a.equal?(b) a == b
PHP $a === $b $a == $b
Dart identical(a, b) a == b

For a class that defines no equality of its own, most languages fall back to identity: Python’s ==, Java’s equals and C#’s Equals all compare references until the class says otherwise. That is why the program compared the balances rather than the cards. Teaching a class what “equal” means, and keeping its hash code consistent with that, is the subject of a later module.

Which question to ask follows from the problem. Identity answers “is this the very object I stored?”, as when a list of open files must not hold the same file twice. Equality answers “do these mean the same thing?”, as for two prices, two dates or two strings, and it is the question most code needs.

Checkpoint

Check: Identity and equality

  1. Question 1 of 2 In Java, a and b are two String objects that both hold "C7", built separately at run time. Which test says they are equal?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: a.equals(b)

    In Java, == on objects is the identity test and equals is the equality test. For strings and boxed numbers, always use equals.

  2. Question 2 of 2 In JavaScript, a and b are two different Card objects that both have balance 90. What does a === b give?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: false, because === on objects is true only for the very same object

    MDN: if both operands are objects, === returns true only if they refer to the same object. That is why the program compared the balances to test equality.

Cached small values make identity tests lie

Numbers and strings are where identity tests go wrong most often, because some runtimes keep one shared object for popular values. Java’s boxing turns an int into an Integer object, and the Java Language Specification guarantees that boxing the same constant from -128 to 127 always gives the identical object, while it “disallows any assumptions” about other values. Run on the pinned JDK, == happens to be right at 127 and wrong at 128:

Integer objects at 127 and 128 Java · caches/java/IntegerCache.java
public class IntegerCache {
  public static void main(String[] args) {
    // Boxing turns an int into an Integer object; small values come from a cache.
    Integer a = 127;
    Integer b = 127;
    Integer c = 128;
    Integer d = 128;
    System.out.println("127: a == b is " + (a == b) + ", a.equals(b) is " + a.equals(b));
    System.out.println("128: c == d is " + (c == d) + ", c.equals(d) is " + c.equals(d));
  }
}

Output

127: a == b is true, a.equals(b) is true
128: c == d is false, c.equals(d) is true

Recorded with OpenJDK 25.0.4.1 LTS (Eclipse Temurin) on macOS 26 arm64. To run it yourself: java IntegerCache.java

equals was right both times. CPython does something similar for small integers, as an implementation detail of its C code: version 3.14 keeps one object for each integer from -5 to 256. The program builds each number while it runs, with int("256"), so that no constant in the source can be shared:

Two ints built from the same text Python · caches/python/int_cache.py
# int(text) builds each number while the program runs, so no constant is shared.
for text in ["256", "257"]:
    a = int(text)
    b = int(text)
    print(f"{text}: a == b is {a == b}, a is b is {a is b}")

Output

256: a == b is True, a is b is True
257: a == b is True, a is b is False

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 int_cache.py

At 256, a is b is True, and at 257 it is False, while a == b is True both times. Press Edit and try "-6" and "-5": the cache starts at -5.

Python 3.15

The Python 3.15 documentation gives CPython’s cache as the integers from -5 to 1024, so the same program prints True for 257 there. A result that changes between versions was never a rule: compare numbers and strings with ==. Since Python 3.8 the compiler even warns, with a SyntaxWarning, when is is used with a number or string literal.

Checkpoint

Check: Caches that fool identity tests

  1. One question Java runs Integer x = 1000; Integer y = 1000; on the pinned JDK. What do x == y and x.equals(y) give?

    Choose one answer.

    Show the answer: One question

    Answer: x == y is false and x.equals(y) is true

    The Java Language Specification guarantees one shared object only from -128 to 127 and allows no assumptions about other values. The recorded run showed false at 128; 1000 behaves the same way on this JDK.

Practise: stop a function from changing its caller’s list

The exercise gives you a function that returns the right numbers and quietly damages its caller’s data: it writes the new marks into the list it was given, through the reference it received. Make it build a new list instead. The sample tests run in your browser and compare the caller’s list before and after the call:

Exercise · Easy · Python, JavaScript

Return new marks without changing the caller's list

with_bonus(marks, bonus) (JavaScript: withBonus(marks, bonus)) should return the marks with bonus added to each, none above 100: with_bonus([72, 95, 40], 10) is [82, 100, 50]. The starter code returns the right numbers, but it writes them into the list it was given, so the caller's own list changes too, through the reference the function received.

Fix it so that:

  • the result is a new list with every mark plus bonus, capped at 100;
  • the caller's list is exactly as it was, and the result is not that same list;
  • an empty list gives an empty list.

The sample tests check the values, keep a reference to the caller's list and compare it before and after the call, and check that the result and the argument are different objects.

Python · Starter code · bonus.py

def with_bonus(marks: list[int], bonus: int) -> list[int]:
    """The marks with bonus added to each, at most 100. The caller's list must not change."""
    for i in range(len(marks)):
        marks[i] = min(100, marks[i] + bonus)
    return marks
The sample tests · test_bonus.py
from bonus import with_bonus


def test_values():
    """every mark gets the bonus, and none goes above 100"""
    assert with_bonus([72, 95, 40], 10) == [82, 100, 50]
    assert with_bonus([100, 0], 5) == [100, 5]
    assert with_bonus([], 10) == [], "an empty list gives an empty list"


def test_callers_list_is_unchanged():
    """the caller's list holds the same marks after the call"""
    marks = [72, 95, 40]
    before = list(marks)
    with_bonus(marks, 10)
    assert marks == before, f"the caller's list changed from {before} to {marks}"


def test_result_is_a_new_list():
    """the result is a different list object from the argument"""
    marks = [60, 70]
    result = with_bonus(marks, 5)
    assert result is not marks, "the function returned the caller's own list"
    result.append(99)
    assert marks == [60, 70], "changing the result must not change the caller's list"

JavaScript · Starter code · bonus.mjs

/** The marks with bonus added to each, at most 100. The caller's array must not change. */
export function withBonus(marks, bonus) {
  for (let i = 0; i < marks.length; i++) {
    marks[i] = Math.min(100, marks[i] + bonus);
  }
  return marks;
}
The sample tests · bonus.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { withBonus } from './bonus.mjs';

test('every mark gets the bonus, and none goes above 100', () => {
  assert.deepEqual(withBonus([72, 95, 40], 10), [82, 100, 50]);
  assert.deepEqual(withBonus([100, 0], 5), [100, 5]);
  assert.deepEqual(withBonus([], 10), [], 'an empty array gives an empty array');
});

test("the caller's array holds the same marks after the call", () => {
  const marks = [72, 95, 40];
  const before = [...marks];
  withBonus(marks, 10);
  assert.deepEqual(marks, before, "the caller's array changed");
});

test('the result is a different array from the argument', () => {
  const marks = [60, 70];
  const result = withBonus(marks, 5);
  assert.ok(result !== marks, "the function returned the caller's own array");
  result.push(99);
  assert.deepEqual(marks, [60, 70], "changing the result must not change the caller's array");
});
A hint

Build the answer in a list of your own instead of assigning to marks[i]: start from an empty list and append each new mark, or use a comprehension (JavaScript: marks.map(...)). Reading the caller's list is fine; writing to it is what the caller can see.

The sample tests run on this device, in your browser (Pyodide, QuickJS): nothing is sent to mysmartcopilot.com. The first run of each language downloads it: Python (about 13.5 MB) or JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Python Online Compiler Make two names for one list and change it through each, or try is and == on numbers of your own.

The questions cover all five ideas. The weakest one comes first, and every right answer makes the next question a little harder:

Practice that adapts to you

Practise this lesson

10 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 10 In Python or Java, what does a variable that names an object hold?

    Choose one answer.

    Show the answer: Question 1 of 10

    Answer: A reference to the object, which lives somewhere else

    A reference is how a variable reaches an object. Several variables can hold references to one object, which is what makes aliases possible.

  2. Question 2 of 10 In Java, Card c = null; c.pay(10); What happens?

    Choose one answer.

    Show the answer: Question 2 of 10

    Answer: A NullPointerException, because c refers to no object

    null is a reference to no object. Kotlin, Swift and Rust make "maybe no object" a separate type so the compiler catches this; Java, Python and JavaScript discover it only at run time.

  3. Question 3 of 10 In which of these does b = a copy a card whose type is the language's usual struct or record?

    Choose every answer that is right.

    Show the answer: Question 3 of 10

    Answer:

    • C++
    • Go
    • Swift, when Card is a struct

    C, C++, Go and Swift structs are copied, and Rust copies Copy types; the class-based languages share.

  4. Question 4 of 10 In Rust, Card does not derive Copy. What happens to a after let b = a?

    Choose one answer.

    Show the answer: Question 4 of 10

    Answer: It is moved into b, and the compiler refuses any later use of a

    Rust's ownership rules: let b = a moves a value that is not Copy. The lesson's Rust card derives Copy, which is why its tab printed a.balance = 100.

  5. Question 5 of 10 What does a Java method receive when it is called with an object?

    Choose one answer.

    Show the answer: Question 5 of 10

    Answer: A copy of the reference to the object

    The method and the caller hold two references to one object. Changing the object is visible to both; pointing one reference elsewhere is not.

  6. Question 6 of 10 In C++, void replace(Card& card) { card = Card{500}; } is called as replace(mine). What is mine.balance afterwards?

    Choose one answer.

    Show the answer: Question 6 of 10

    Answer: 500, because Card& is a true alias of the caller's variable, so assigning to it replaces mine

    Assignment through a reference copies into the object it refers to, here the caller's card. That is why the lesson's C++ tab used pointers to mirror what Java and Python pass.

  7. Question 7 of 10 Which Python test asks whether two names reach the same object?

    Choose one answer.

    Show the answer: Question 7 of 10

    Answer: a is b

    The data model: the is operator compares the identity of two objects. Use it for None and for the very same object, and == for values.

  8. Question 8 of 10 A Python class Card defines no __eq__. Card(90) == Card(90) gives what?

    Choose one answer.

    Show the answer: Question 8 of 10

    Answer: False, because without __eq__ Python's == falls back to identity, and these are two objects

    Java's equals and C#'s Equals behave the same way by default. Giving a class its own equality is the subject of a later module.

  9. Question 9 of 10 Which Integer values does the Java Language Specification guarantee to box into one shared object?

    Choose one answer.

    Show the answer: Question 9 of 10

    Answer: From -128 to 127

    Inside that range == happens to agree with equals; outside it the specification allows no assumptions. Compare boxed numbers with equals.

  10. Question 10 of 10 On CPython 3.14, a = int("257") and b = int("257"). Why is a is b False while a == b is True?

    Choose one answer.

    Show the answer: Question 10 of 10

    Answer: CPython 3.14 shares objects only for integers from -5 to 256, so int("257") makes a new object each time

    CPython keeps one object for each integer from -5 to 256 in 3.14, and up to 1024 in 3.15. Because the range can change, is must never be used to compare numbers.

Interview questions

Fresher · Is Java pass by value or pass by reference?

Java passes every argument by value, with no exceptions. An int argument arrives as a copy of the number, and an object argument arrives as a copy of the reference to the object, so the method holds its own reference. Two things follow. The method can change the object through the copy, card.balance -= 10, and the caller sees the change, because both references point at one object. The method cannot make the caller’s variable point at another object: card = new Card(500) changes only its own copy, which is why a swap(a, b) method that makes the caller’s two variables trade objects is impossible in Java. In the lesson’s program, charge_fee left mine at 90 and swap_card could not replace it. “Pass by reference” would mean the method receives the caller’s variable itself, as C++’s Card& or C#’s ref do.

Three follow-ups an interviewer might ask next, each with four answers to weigh:

Checkpoint

Interview round: three follow-up questions

  1. Question 1 of 3 The interviewer asks: "Write a Java method swap(Card a, Card b) that swaps the caller's two variables." What do you say?

    Choose one answer.

    Show the answer: Question 1 of 3

    Answer: It cannot be done. Java passes each reference by value, so the method can swap its own copies but not the caller's variables; return a pair, or swap the fields instead

    In C++ a swap of the caller's variables takes Card& parameters, as std::swap does. Java has no such parameters, so a swap must exchange the cards' fields or return the swapped pair for the caller to assign.

  2. Question 2 of 3 def add(item, items=[]): items.append(item); return items. Calling add(1) and then add(2) returns what the second time, and why?

    Choose one answer.

    Show the answer: Question 2 of 3

    Answer: [1, 2], because the default list is made once, when def runs, and every call that omits items shares it

    The default value is created when the def statement runs and kept with the function, so every call that leaves out items appends to the same list. Write items=None and make a new list inside the function.

  3. Question 3 of 3 cards is a Python list of Card objects. copy = list(cards), then copy[0].balance -= 50. Does cards[0].balance change?

    Choose one answer.

    Show the answer: Question 3 of 3

    Answer: Yes. list(cards) makes a new list holding the same references, so copy[0] and cards[0] are one card

    A shallow copy gives you a new container of the same references. Appending to copy leaves cards alone, but changing an object inside it is seen through both lists.

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to mid

    A helper normalise(marks) is meant to return a cleaned copy of a list of marks, yet after it runs in production the caller's own list has changed. How do you find and fix the bug, and how do you stop it from coming back?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A Java service compares order IDs held as Integer objects with ==. Every test passes, and in production equal IDs are reported as different. Explain why, fix it, and say how Java, Python and JavaScript separate identity from equality.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • A variable of a class type holds a reference: after b = a the names are aliases of one object, and a change through either is seen through both.
  • Whether b = a shares or copies depends on the language and the type: classes share in Python, Java, JavaScript, C# and Kotlin, while the structs of C, C++, Go and Swift are copied and Rust moves its values unless they are Copy.
  • Arguments are passed by value, and for an object that value is a reference: a function can change the caller’s object but cannot make the caller’s variable point at a new one, as swap_card showed.
  • Identity asks whether two names reach the same object and equality whether two values mean the same thing; each language spells the two tests differently, and most code needs equality.
  • Caches make identity look like equality for small values, Java’s Integer from -128 to 127 and CPython’s integers from -5 to 256 in 3.14, so compare numbers and strings with equals or ==.

Terms in this lesson

alias
A second name for the same object. After b = a in Python or Java, b is an alias of a, and a change made through either name is seen through both.
reference
A value that leads to an object stored elsewhere. In Python, Java and JavaScript a variable of an object type holds a reference, so copying the variable copies the reference, not the object.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.