Skip to content

Commit 6de9ccd

Browse files
authored
Merge pull request #4 from lucasbellesi/codex/java-smart-pointers-expert
Add Java smart pointers expert module
2 parents 1f576f3 + 32ccf2d commit 6de9ccd

14 files changed

Lines changed: 399 additions & 65 deletions

CONCEPT_INDEX.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Use this page to browse the same concept across every active language track.
4747
| Concept | C++ | C# | Go | Java | Python | TypeScript |
4848
| --- | --- | --- | --- | --- | --- | --- |
4949
| memory-management-and-raii | [C++](languages/cpp/04-expert/memory-management-and-raii/README.md) | [C#](languages/csharp/04-expert/memory-management-and-raii/README.md) | [Go](languages/go/04-expert/memory-management-and-raii/README.md) | [Java](languages/java/04-expert/memory-management-and-raii/README.md) | [Python](languages/python/04-expert/memory-management-and-raii/README.md) | [TypeScript](languages/typescript/04-expert/memory-management-and-raii/README.md) |
50-
| smart-pointers-in-depth | [C++](languages/cpp/04-expert/smart-pointers-in-depth/README.md) | [C#](languages/csharp/04-expert/smart-pointers-in-depth/README.md) | [Go](languages/go/04-expert/smart-pointers-in-depth/README.md) | Planned | [Python](languages/python/04-expert/smart-pointers-in-depth/README.md) | [TypeScript](languages/typescript/04-expert/smart-pointers-in-depth/README.md) |
50+
| smart-pointers-in-depth | [C++](languages/cpp/04-expert/smart-pointers-in-depth/README.md) | [C#](languages/csharp/04-expert/smart-pointers-in-depth/README.md) | [Go](languages/go/04-expert/smart-pointers-in-depth/README.md) | [Java](languages/java/04-expert/smart-pointers-in-depth/README.md) | [Python](languages/python/04-expert/smart-pointers-in-depth/README.md) | [TypeScript](languages/typescript/04-expert/smart-pointers-in-depth/README.md) |
5151
| concurrency-basics | [C++](languages/cpp/04-expert/concurrency-basics/README.md) | [C#](languages/csharp/04-expert/concurrency-basics/README.md) | [Go](languages/go/04-expert/concurrency-basics/README.md) | Planned | [Python](languages/python/04-expert/concurrency-basics/README.md) | [TypeScript](languages/typescript/04-expert/concurrency-basics/README.md) |
5252
| performance-and-profiling-basics | [C++](languages/cpp/04-expert/performance-and-profiling-basics/README.md) | [C#](languages/csharp/04-expert/performance-and-profiling-basics/README.md) | [Go](languages/go/04-expert/performance-and-profiling-basics/README.md) | Planned | [Python](languages/python/04-expert/performance-and-profiling-basics/README.md) | [TypeScript](languages/typescript/04-expert/performance-and-profiling-basics/README.md) |
5353
| modularization-and-build-structure | [C++](languages/cpp/04-expert/modularization-and-build-structure/README.md) | [C#](languages/csharp/04-expert/modularization-and-build-structure/README.md) | [Go](languages/go/04-expert/modularization-and-build-structure/README.md) | Planned | [Python](languages/python/04-expert/modularization-and-build-structure/README.md) | [TypeScript](languages/typescript/04-expert/modularization-and-build-structure/README.md) |

LANGUAGE_PARITY_MATRIX.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This matrix tracks module and checkpoint parity across C++, C#, Go, Java, Python
44

55
- Canonical order is defined by the C++ track.
66
- C++, C#, Go, Python, and TypeScript now reach module and checkpoint parity through `04-expert`.
7-
- Java currently tracks `01-foundations`, `02-core`, all `03-advanced` modules and checkpoints, and the first `04-expert` module as a Java 21 MVP.
7+
- Java currently tracks `01-foundations`, `02-core`, all `03-advanced` modules and checkpoints, and the first two `04-expert` modules as a Java 21 MVP.
88
- Status labels:
99
- `Done`: module implemented with example, exercises, and README.
1010
- `Planned`: module not implemented yet, already queued in order.
@@ -47,7 +47,7 @@ Current parity progress in non-C++ tracks:
4747

4848
- C#: `5/5` modules complete in `03-advanced`, `5/5` in `04-expert`
4949
- Go: `5/5` modules complete in `03-advanced`, `5/5` in `04-expert`
50-
- Java: `5/5` modules complete in `03-advanced`, `1/5` in `04-expert`
50+
- Java: `5/5` modules complete in `03-advanced`, `2/5` in `04-expert`
5151
- Python: `5/5` modules complete in `03-advanced`, `5/5` in `04-expert`
5252
- TypeScript: `5/5` modules complete in `03-advanced`, `5/5` in `04-expert`
5353

@@ -68,7 +68,7 @@ Current parity progress in non-C++ tracks:
6868
| Order | Module | C++ | C# | Go | Java | Python | TypeScript |
6969
| --- | --- | --- | --- | --- | --- | --- | --- |
7070
| 1 | memory-management-and-raii | Done | Done | Done | Done | Done | Done |
71-
| 2 | smart-pointers-in-depth | Done | Done | Done | Planned | Done | Done |
71+
| 2 | smart-pointers-in-depth | Done | Done | Done | Done | Done | Done |
7272
| 3 | concurrency-basics | Done | Done | Done | Planned | Done | Done |
7373
| 4 | performance-and-profiling-basics | Done | Done | Done | Planned | Done | Done |
7474
| 5 | modularization-and-build-structure | Done | Done | Done | Planned | Done | Done |

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ If you only want to learn one track, follow that track README first because lang
5656
| C++ | 00-setup, 01-foundations, 02-core, 03-advanced, 04-expert | Foundations, Core, Advanced, Expert, projects, assessments | Most complete and primary track |
5757
| C# | 01-foundations, 02-core, 03-advanced, 04-expert | 8/8 foundations modules, 6/6 core modules, 5/5 advanced modules, 5/5 expert modules, 4/4 projects, 4/4 assessments | Module and checkpoint parity complete through expert |
5858
| Go | 01-foundations, 02-core, 03-advanced, 04-expert | 8/8 foundations modules, 6/6 core modules, 5/5 advanced modules, 5/5 expert modules, 4/4 projects, 4/4 assessments | Module and checkpoint parity complete through expert |
59-
| Java | 01-foundations, 02-core, 03-advanced, 04-expert partial | 8/8 foundations modules, 6/6 core modules, 5/5 advanced modules, 1/5 expert modules, 3/4 projects, 3/4 assessments | Java 21 MVP complete through advanced checkpoints plus first expert module |
59+
| Java | 01-foundations, 02-core, 03-advanced, 04-expert partial | 8/8 foundations modules, 6/6 core modules, 5/5 advanced modules, 2/5 expert modules, 3/4 projects, 3/4 assessments | Java 21 MVP complete through advanced checkpoints plus two expert modules |
6060
| Python | 01-foundations, 02-core, 03-advanced, 04-expert | 8/8 foundations modules, 6/6 core modules, 5/5 advanced modules, 5/5 expert modules, 4/4 projects, 4/4 assessments | Module and checkpoint parity complete through expert |
6161
| TypeScript | 01-foundations, 02-core, 03-advanced, 04-expert | 8/8 foundations modules, 6/6 core modules, 5/5 advanced modules, 5/5 expert modules, 4/4 projects, 4/4 assessments | Module and checkpoint parity complete through expert |
6262

STUDY_PLAN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This plan assumes 5 study days per week, 60-90 minutes per day.
44

5-
This is the canonical study plan because the C++ track is the primary complete reference. Learners using C#, Go, Python, TypeScript, or Java can follow the same weekly concept order by opening the matching language guide and concept paths from [CONCEPT_INDEX.md](CONCEPT_INDEX.md). Java currently has complete foundations, core, advanced modules and checkpoints, plus the first expert module.
5+
This is the canonical study plan because the C++ track is the primary complete reference. Learners using C#, Go, Python, TypeScript, or Java can follow the same weekly concept order by opening the matching language guide and concept paths from [CONCEPT_INDEX.md](CONCEPT_INDEX.md). Java currently has complete foundations, core, advanced modules and checkpoints, plus the first two expert modules.
66

77
## How To Use This Plan
88

languages/java/04-expert/README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,39 @@
11
# Java 04 Expert
22

3-
This level starts the expert Java track with deterministic resource cleanup and clear boundaries between garbage-collected memory and external resources.
3+
This level starts the expert Java track with deterministic resource cleanup, clear boundaries between garbage-collected memory and external resources, and managed-reference ownership patterns.
44

55
## Learning Metadata
66

77
- Difficulty: Advanced.
8-
- Estimated Time: 45-60 minutes for the current module.
8+
- Estimated Time: 45-60 minutes per module.
99
- Prerequisites: Completion of `01-foundations`, `02-core`, and `03-advanced`.
10-
- Study Strategy: Focus on resource lifetime first, then compare Java cleanup patterns with RAII, `defer`, context managers, and disposable objects in other tracks.
10+
- Study Strategy: Focus on resource lifetime first, then compare Java cleanup, ownership slots, defensive snapshots, and weak references with equivalent patterns in other tracks.
1111

1212
## Module Order
1313

1414
1. [memory-management-and-raii](./memory-management-and-raii/README.md)
15+
2. [smart-pointers-in-depth](./smart-pointers-in-depth/README.md)
1516

1617
## Level Outcomes
1718

1819
- Explain why Java garbage collection does not replace deterministic resource cleanup.
1920
- Use `try-with-resources` with `AutoCloseable`.
2021
- Make `close` operations idempotent.
2122
- Guard resource methods after close.
23+
- Model reference ownership transfer by clearing the old owner.
24+
- Use defensive snapshots and weak references deliberately.
2225
- Compare Java cleanup with C++, C#, Go, Python, and TypeScript.
2326

2427
## Done When
2528

2629
- [ ] I ran `memory-management-and-raii/example/Main.java`.
2730
- [ ] I completed both `memory-management-and-raii` exercises.
31+
- [ ] I ran `smart-pointers-in-depth/example/Main.java`.
32+
- [ ] I completed both `smart-pointers-in-depth` exercises.
2833
- [ ] I can explain why file handles, sockets, and buffers should be closed predictably.
2934
- [ ] I can implement a small `AutoCloseable` resource.
35+
- [ ] I can explain when Java references are owning, shared, or weak by convention.
3036

3137
## Study Tip
3238

33-
Java's garbage collector manages memory, but it does not make external resources close at the exact time your program logic needs. Use `try-with-resources` when cleanup timing matters.
39+
Java's garbage collector manages memory, but it does not make external resources close at the exact time your program logic needs. Use `try-with-resources` when cleanup timing matters, and make ownership boundaries visible when several references can reach the same object.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Smart Pointers in Depth (Java)
2+
3+
This module adapts smart pointer ideas to Java through strong references, explicit ownership slots, defensive copies, `Optional`, and `WeakReference`.
4+
5+
## Learning Metadata
6+
7+
- Difficulty: Advanced.
8+
- Estimated Time: 45-60 minutes.
9+
- Prerequisites: `04-expert/memory-management-and-raii`.
10+
- Cross-Language Lens: Compare explicit ownership tools in C++ with managed references, pointer conventions, and weak-reference patterns elsewhere.
11+
12+
## Quick Run
13+
14+
~~~bash
15+
javac -d build/java languages/java/04-expert/smart-pointers-in-depth/example/Main.java
16+
java -cp build/java Main
17+
~~~
18+
19+
## Topics Covered
20+
21+
- Strong references as normal object ownership in Java.
22+
- Moving a reference between explicit owner slots.
23+
- Clearing old owners so ownership transfer is visible.
24+
- Returning defensive snapshots instead of mutable internals.
25+
- Using `Optional` for empty ownership slots.
26+
- Using `WeakReference` for non-owning observation.
27+
28+
## Common Pitfalls
29+
30+
- Assuming garbage collection makes ownership design irrelevant.
31+
- Keeping accidental strong references in caches or observers.
32+
- Returning mutable internal collections instead of defensive snapshots.
33+
- Using `WeakReference` where a normal strong dependency is simpler.
34+
- Treating `Optional` as storage instead of an API boundary signal.
35+
36+
## Cross-Language Notes
37+
38+
- Java does not expose C++-style smart pointers, so the comparison moves to references, ownership protocols, defensive copying, and `WeakReference`.
39+
- Compared with C++, ownership is usually expressed through object boundaries and method contracts rather than pointer types.
40+
- Compared with C# and Python, Java makes empty slots explicit with `Optional` and named reference types.
41+
- The learner goal is to make aliasing, ownership transfer, and weak observation visible in a managed runtime.
42+
43+
## Exercise Focus
44+
45+
- exercises/Exercise01.java: move an owned document reference between holders.
46+
- exercises/Exercise02.java: observe cache entries through `WeakReference`.
47+
48+
### Exercise Specs
49+
50+
1. exercises/Exercise01.java
51+
- Input: none.
52+
- Output: ownership transfer logs before and after moving.
53+
- Edge cases: moving from an empty owner; destination already holding another object.
54+
55+
2. exercises/Exercise02.java
56+
- Input: none.
57+
- Output: alive/expired cache lookup logs.
58+
- Edge cases: expired weak reference; cache miss.
59+
60+
## Checkpoint
61+
62+
- [ ] I can explain strong vs weak references in Java.
63+
- [ ] I can model an ownership transfer by clearing the previous owner.
64+
- [ ] I can return defensive snapshots instead of shared mutable internals.
65+
- [ ] I can choose when a weak observer is appropriate.
66+
- [ ] I completed exercises/Exercise01.java.
67+
- [ ] I completed exercises/Exercise02.java.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Module focus: Tracking ownership and lifetime when multiple references can observe the same value.
2+
// Why it matters: practicing smart pointers in depth patterns makes exercises and checkpoints easier to reason about.
3+
4+
import java.lang.ref.WeakReference;
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
import java.util.Optional;
8+
9+
public class Main {
10+
record Report(String title) {
11+
}
12+
13+
static final class ReportOwner {
14+
private final String name;
15+
private Report current;
16+
17+
ReportOwner(String name, Report current) {
18+
this.name = name;
19+
this.current = current;
20+
}
21+
22+
Optional<Report> current() {
23+
// Optional keeps "empty owner" checks explicit at API boundaries.
24+
return Optional.ofNullable(current);
25+
}
26+
27+
void transferTo(ReportOwner destination) {
28+
// Empty owners cannot transfer ownership, so the method exits visibly.
29+
if (current == null) {
30+
System.out.println(name + " has nothing to transfer.");
31+
return;
32+
}
33+
34+
// Moving the reference leaves the source empty and the destination responsible.
35+
System.out.println(name + " transfers " + current.title() + " to " + destination.name + ".");
36+
destination.current = current;
37+
current = null;
38+
}
39+
40+
void describe() {
41+
System.out.println(name + ": " + current().map(Report::title).orElse("empty"));
42+
}
43+
}
44+
45+
static final class ReportArchive {
46+
private final List<Report> reports = new ArrayList<>();
47+
48+
void add(Report report) {
49+
// Strong references in this list keep archived reports alive.
50+
reports.add(report);
51+
}
52+
53+
List<Report> snapshot() {
54+
// A defensive snapshot prevents callers from mutating archive internals.
55+
return List.copyOf(reports);
56+
}
57+
}
58+
59+
static final class PreviewPane {
60+
private final WeakReference<Report> currentReport;
61+
62+
PreviewPane(Report report) {
63+
// The preview can observe the report without becoming an owner.
64+
this.currentReport = new WeakReference<>(report);
65+
}
66+
67+
void clearForDemo() {
68+
// Clearing the weak reference gives this small example deterministic output.
69+
currentReport.clear();
70+
}
71+
72+
void describe() {
73+
Report report = currentReport.get();
74+
if (report == null) {
75+
System.out.println("Preview target expired.");
76+
return;
77+
}
78+
79+
System.out.println("Preview can still see: " + report.title());
80+
}
81+
}
82+
83+
public static void main(String[] args) {
84+
ReportOwner inbox = new ReportOwner("Inbox", new Report("Quarterly Summary"));
85+
ReportOwner archiveSlot = new ReportOwner("Archive", null);
86+
87+
// Report ownership before and after transfer so the lifetime change is visible.
88+
inbox.describe();
89+
archiveSlot.describe();
90+
inbox.transferTo(archiveSlot);
91+
inbox.describe();
92+
archiveSlot.describe();
93+
94+
ReportArchive archive = new ReportArchive();
95+
archiveSlot.current().ifPresent(archive::add);
96+
archive.add(new Report("Budget Notes"));
97+
// The snapshot is safe to share because callers cannot mutate archive internals.
98+
System.out.println("Archive snapshot size: " + archive.snapshot().size());
99+
100+
Report transientDraft = new Report("Transient Draft");
101+
PreviewPane preview = new PreviewPane(transientDraft);
102+
preview.describe();
103+
104+
// Weak observers must handle the target disappearing without owning it.
105+
transientDraft = null;
106+
preview.clearForDemo();
107+
preview.describe();
108+
}
109+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Module focus: Moving one owned reference between explicit holders.
2+
// Why it matters: managed runtimes still need clear ownership boundaries.
3+
4+
import java.util.Optional;
5+
6+
public class Exercise01 {
7+
record Document(String name) {
8+
}
9+
10+
static final class DocumentSlot {
11+
private final String label;
12+
private Document current;
13+
14+
DocumentSlot(String label, Document current) {
15+
this.label = label;
16+
this.current = current;
17+
}
18+
19+
Optional<Document> current() {
20+
return Optional.ofNullable(current);
21+
}
22+
23+
void moveTo(DocumentSlot destination) {
24+
if (current == null) {
25+
System.out.println(label + " is empty.");
26+
return;
27+
}
28+
29+
System.out.println(label + " moves " + current.name() + " to " + destination.label + ".");
30+
destination.current = current;
31+
current = null;
32+
}
33+
34+
void print() {
35+
System.out.println(label + ": " + current().map(Document::name).orElse("empty"));
36+
}
37+
}
38+
39+
public static void main(String[] args) {
40+
DocumentSlot active = new DocumentSlot("Active", new Document("Roadmap"));
41+
DocumentSlot backup = new DocumentSlot("Backup", new Document("Old Notes"));
42+
DocumentSlot empty = new DocumentSlot("Empty", null);
43+
44+
active.print();
45+
backup.print();
46+
active.moveTo(backup);
47+
active.print();
48+
backup.print();
49+
empty.moveTo(active);
50+
empty.print();
51+
active.print();
52+
}
53+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Module focus: Observing cached values without keeping them alive.
2+
// Why it matters: weak references are useful only when callers expect expiration.
3+
4+
import java.lang.ref.WeakReference;
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
8+
public class Exercise02 {
9+
record CachedValue(String text) {
10+
}
11+
12+
static final class WeakCache {
13+
private final Map<String, WeakReference<CachedValue>> entries = new HashMap<>();
14+
15+
void store(String key, CachedValue value) {
16+
entries.put(key, new WeakReference<>(value));
17+
}
18+
19+
void expireForDemo(String key) {
20+
WeakReference<CachedValue> reference = entries.get(key);
21+
if (reference != null) {
22+
reference.clear();
23+
}
24+
}
25+
26+
void printLookup(String key) {
27+
WeakReference<CachedValue> reference = entries.get(key);
28+
if (reference == null) {
29+
System.out.println(key + ": missing");
30+
return;
31+
}
32+
33+
CachedValue value = reference.get();
34+
if (value == null) {
35+
System.out.println(key + ": expired");
36+
return;
37+
}
38+
39+
System.out.println(key + ": alive -> " + value.text());
40+
}
41+
}
42+
43+
public static void main(String[] args) {
44+
WeakCache cache = new WeakCache();
45+
CachedValue stable = new CachedValue("Still referenced");
46+
CachedValue temporary = new CachedValue("Short lived");
47+
48+
cache.store("stable", stable);
49+
cache.store("temp", temporary);
50+
cache.printLookup("stable");
51+
cache.printLookup("temp");
52+
53+
temporary = null;
54+
cache.expireForDemo("temp");
55+
56+
cache.printLookup("stable");
57+
cache.printLookup("temp");
58+
cache.printLookup("missing");
59+
}
60+
}

0 commit comments

Comments
 (0)