diff --git a/examples/stage0/snippets/classes-methods/Point.java b/examples/stage0/snippets/classes-methods/Point.java new file mode 100644 index 0000000..2a34dfc --- /dev/null +++ b/examples/stage0/snippets/classes-methods/Point.java @@ -0,0 +1,55 @@ +/* + * Copyright 2026 FRCSoftware + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// [pointFull] +class Point { + // [finalFields] + // Each Point has its own x and y coordinates, and they never change. + private final double x; + private final double y; + // [/finalFields] + // [staticConstant] + // Shared by every Point, instead of belonging to just one instance. + public static final Point ORIGIN = new Point(0, 0); + // [/staticConstant] + + // [constructor] + // Sets this Point's coordinates to the given x and y values. + public Point(double x, double y) { + this.x = x; + this.y = y; + } + // [/constructor] + + // [getters] + // Let other classes read the private x and y fields. + public double getX() { return this.x; } + public double getY() { return this.y; } + // [/getters] + + // [plus] + // Adds this Point's coordinates to another Point's coordinates. + public Point plus(Point other) { + return new Point(this.x + other.x, this.y + other.y); + } + // [/plus] + + // [minus] + // Subtracts another Point's coordinates from this Point's coordinates. + public Point minus(Point other) { + return new Point(this.x - other.x, this.y - other.y); + } + // [/minus] + + // [norm] + // Computes this Point's distance from the origin. + public double norm() { + double sumOfSquares = this.x * this.x + this.y * this.y; + return Math.sqrt(sumOfSquares); + } + // [/norm] +} +// [/pointFull] diff --git a/examples/stage0/snippets/classes-methods/RobotTracker.java b/examples/stage0/snippets/classes-methods/RobotTracker.java new file mode 100644 index 0000000..96e4cac --- /dev/null +++ b/examples/stage0/snippets/classes-methods/RobotTracker.java @@ -0,0 +1,57 @@ +/* + * Copyright 2026 FRCSoftware + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// [robotTrackerFull] +class RobotTracker { + // [mutableField] + // Not final, since the robot's position changes as it moves. + private Point position; + // [/mutableField] + + // [robotTrackerConstructor] + // Starts tracking from a given position. + public RobotTracker(Point startPosition) { + this.position = startPosition; + } + // [/robotTrackerConstructor] + + // [constructorChaining] + // Starts tracking from the origin by default. + public RobotTracker() { + this(Point.ORIGIN); + } + // [/constructorChaining] + + // [stateMutation] + // Moves the tracked position by delta. + public void move(Point delta) { + this.position = this.position.plus(delta); + } + // [/stateMutation] + + // [localVariableChain] + // Finds the straight-line distance from the current position to target. + public double distanceTo(Point target) { + Point diff = target.minus(this.position); + return diff.norm(); + } + // [/localVariableChain] + + // [getPosition] + // Lets other classes read the current position. + public Point getPosition() { + return this.position; + } + // [/getPosition] + + // [useStaticConstant] + // Moves the tracked position back to the origin. + public void reset() { + this.position = Point.ORIGIN; + } + // [/useStaticConstant] +} +// [/robotTrackerFull] diff --git a/examples/stage0/snippets/classes-methods/Usage.java b/examples/stage0/snippets/classes-methods/Usage.java new file mode 100644 index 0000000..e1e132d --- /dev/null +++ b/examples/stage0/snippets/classes-methods/Usage.java @@ -0,0 +1,30 @@ +/* + * Copyright 2026 FRCSoftware + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +void main() { + // [createAndUse] + Point a = new Point(3, 4); + Point b = new Point(1, 2); + + Point sum = a.plus(b); + System.out.println(sum.getX()); // 4.0 + System.out.println(sum.getY()); // 6.0 + System.out.println(a.norm()); // 5.0 + // [/createAndUse] + + // [accessStaticConstant] + System.out.println(Point.ORIGIN.getX()); // 0.0 + // [/accessStaticConstant] + + // [trackerUsage] + RobotTracker tracker = new RobotTracker(Point.ORIGIN); + tracker.move(new Point(3, 0)); + tracker.move(new Point(0, 4)); + System.out.println(tracker.distanceTo(Point.ORIGIN)); // 5.0 + tracker.reset(); + System.out.println(tracker.getPosition().getX()); // 0.0 + // [/trackerUsage] +} diff --git a/src/config/sidebarConfig.ts b/src/config/sidebarConfig.ts index bc56f1e..7ebdeb0 100644 --- a/src/config/sidebarConfig.ts +++ b/src/config/sidebarConfig.ts @@ -84,10 +84,10 @@ export const sidebarSections: Record = { // label: 'Loops', // slug: 'learning-course/stage0/loops', // }, - // { - // label: 'Objects', - // slug: 'learning-course/stage0/objects', - // }, + { + label: 'Classes, Fields, and Methods', + slug: 'learning-course/stage0/classes-methods', + }, // { // label: 'Methods', // slug: 'learning-course/stage0/methods', diff --git a/src/content/docs/learning-course/stage0/classes-methods.mdx b/src/content/docs/learning-course/stage0/classes-methods.mdx new file mode 100644 index 0000000..5cf9288 --- /dev/null +++ b/src/content/docs/learning-course/stage0/classes-methods.mdx @@ -0,0 +1,350 @@ +--- +title: Classes, Fields, and Methods +description: An Introduction to Java classes and objects, as well as their members (fields and methods) +prev: learning-course/stage0/conditionals +next: false +codeRegionSources: + point: stage0/snippets/classes-methods/Point.java + tracker: stage0/snippets/classes-methods/RobotTracker.java + usage: stage0/snippets/classes-methods/Usage.java +--- + +In an [earlier lesson](/learning-course/stage0/java-fundamentals/), we discussed different data types in Java, +such as `int`, `double`, and `String`. +In this lesson, we're going to learn about classes, which are a way to create our own data types, +how to use them to create objects, and how to define a class's components: fields and methods. + +## Types of Types + +The Java programming language has two types of types: **primitive types** and **reference types**. +**Primitive types** are the most basic types that are built into the language, +such as `int`, `double`, and `boolean`. +**Reference types** (also called object types) are more complex types that are defined by programmers. +Many object types come with the Java Development Kit (JDK), such as `String`, +and other libraries, +but you can also define your own object types. + +Classes are a way to define object types. +A **class** is a template for creating **object**s, +with **fields** and **methods** that define the object's state and behavior. +An object is an instance of a class. + +Before writing any code, it helps to have a mental picture of what an object actually is. +So far, all the code you've written has been from the perspective of a single actor: the computer running your program. +For instance, `System.out.println("hello");` reads like an instruction: "tell the computer to print hello." + +Classes let you write code from a different perspective, sometimes called **object-oriented programming**. +Instead of one actor doing everything, your program is made up of many objects, each with its own **state** and **behavior**, +similar to characters in a play. +A character has attributes, like a costume or a name, and capabilities, like singing or speaking a line. +In code, an object's attributes are stored in its **fields**, and its capabilities are defined by its **methods**. + +In FRC code, objects are usually more abstract than characters in a play. +A `Point` represents a location, a `Motor` represents a physical motor, and so on. +Objects are also just a convenient way to group related data and behavior together, +so it doesn't always have to belong to a real-world thing. + + + +## Defining Our Own Classes + +Now that we know what a class is, let's define our own class. +Our first example will be a simple `Point` class that represents a point in a two-dimensional space. +This will help us write code that lets our robot know where on the field it is. + +In Java, every class is defined in a `.java` file, and starts with the `public class` keywords. +The `public` keyword means the class can be accessed from anywhere in the program, including other files. +We'll see the `public` keyword again later in this lesson, applied to fields and methods instead of a whole class. +The `class` keyword tells Java we're defining a new class, followed by the name we're giving it, `Point`. + +Let's create a new file called `Point.java` and start it like this: + +{/* rli:ignore */} + +```java +public class Point { + +} +``` + +Then, we're going to add three fields. +A **field** is a variable that belongs to a class, and stores part of an object's state. +Every `Point` object will have its own copy of each field, so two different `Point` objects can have different coordinates. +Firstly, we add two `double` fields, x and y, which represent the point's coordinates. + +```java {point}#finalFields + +``` + +Notice that we used the `final` keyword to make these fields **immutable**. +This means that once they are created, they cannot be changed. +Immutable fields are also called **constant** fields. +A general rule of thumb is to make all fields `final` unless you have a good reason not to, +which we'll see later in this lesson. +We also use the `private` keyword to make these fields **private**. +This means that they can only be accessed by methods in the same class. + +So far, the `x` and `y` fields each belong to a specific `Point` object. +Sometimes, though, we want a value that's shared by every object of a class, instead of one copy per object. +Next, we're going to add a static constant field, ORIGIN, which represents the point at the origin (0, 0). + +```java {point}#staticConstant + +``` + +The `static` keyword is used to make a field **static**. +A static field is a field shared by all instances of a class. +In this case, the ORIGIN field is shared by all Point objects, and it represents the point at the origin (0, 0). +Static fields and methods are accessed using the class name, instead of an instance of the class, +so the ORIGIN field is accessed using `Point.ORIGIN`. +The ORIGIN field is also `public`, so it can be accessed from anywhere in the program. + + + +## Methods and Constructors + +A method is a block of code that is defined inside a class. +Methods are used to define the behavior of the class. +We already saw one example of a method, which is `length()` in the `String` class. +Methods can have parameters, which are values that are passed into the method when it is called. + +When a method finishes, it can give a value back to whoever called it; this is called **returning** a value. +To return a value, a method's body uses the `return` keyword, followed by the value to return. +As soon as a `return` statement runs, the method stops executing immediately and gives that value back to the caller. +For example, the `length()` method in the `String` class returns the length of the string, +and has no parameters. + +The general syntax for a method is: + +{/* rli:ignore */} + +```java +public ReturnType methodName(ParameterType1 param1, ParameterType2 param2, ...) { + // method body +} +``` + +A method can have any number of parameters, including none, and the parameters can be of any type. +`ReturnType` is the type of the value the method returns, such as `int` for `length()`. +A method doesn't have to return something; if it doesn't return anything, +we use the return type `void`, which is a keyword that indicates that the method doesn't return anything. +A `void` method can still use `return` on its own, without a value, to stop early, +but it's more commonly left out entirely, since the method just finishes on its own once it reaches the end. + +A constructor is a special method that is called when an object is created with the `new` keyword. +It is often used to initialize the object's fields. + +To define a constructor, we use the `public` keyword, +followed by the class name, and then a parameter list in parentheses. + +```java {point}#constructor + +``` + +You'll notice that we use the `this` keyword. +The `this` keyword is used to refer to the current object. +It is used to access the object's fields and methods. +In this example, we use the `this` keyword to access the `x` and `y` fields of the current object, +and set them to the values of the parameters `x` and `y`. +Using `this` is necessary here because the parameter names are the same as the field names, +so we use `this.x` to refer to the field `x`, and `x` to refer to the parameter `x`. +`this` isn't always required: if a method's parameters have different names than the fields they set, +Java can already tell them apart, and you can leave `this` out. + +Next we're going to define some getter methods. +Getter methods are methods that simply get, or return, the value of a field. +They have the same return type as the field they return, and no parameters. +Because the `x` and `y` fields are private, we need to define getter methods to +allow other classes to access them: + +```java {point}#getters + +``` + +As you can see, getter methods conventionally start with `get`, followed by the name of the field. + +Next, we're going to define the `plus` method. +The `plus` method takes another `Point` object as a parameter, +and returns a new `Point` object that is the sum of the two `Point` objects. + +```java {point}#plus + +``` + +Here, `other` is just the name we chose for the method's parameter, the other `Point` we're adding. +`this` still refers to the `Point` that `plus` was called on, same as in the constructor. +So `this.x` and `this.y` are the coordinates of the `Point` we called `plus` on, +and `other.x` and `other.y` are the coordinates of the `Point` passed in. + +In this method, we use the `new` keyword to create a new `Point` object. +Because the constructor we defined earlier takes two parameters, +we pass `this.x + other.x` and `this.y + other.y` as the arguments to the constructor. +Therefore, the new `Point` object will have the sum of the two `Point` objects' coordinates. + +The `minus` method works the same way, but subtracts the coordinates instead of adding them, +giving us the vector that points from `other` to this `Point`: + +```java {point}#minus + +``` + +Finally, we're going to define a `norm` method that returns the length of the `Point` object. + +```java {point}#norm + +``` + +This method has a **local variable**, `sumOfSquares`, that stores the sum of the squares of the `x` and `y` fields. +Local variables are variables that are only visible inside the method, +and are used to store intermediate values. +This method also uses the `Math.sqrt()` method, +which is a static method in the `Math` class, +which returns the square root of its argument. + + + +Here is the complete `Point` class: + +```java {point}#pointFull + +``` + +And here is an example of how to use it: + +```java {usage}#createAndUse + +``` + +We create new `Point` objects using the `new` keyword followed by the constructor. +We then call methods on them using the dot operator (`.`): +`a.plus(b)` means "call the `plus` method on the object `a`, passing `b` as the argument." +Inside `plus`, `a` becomes `this` and `b` becomes `other`. +The same pattern applies to any method call: `object.method(arguments)`. + +We can also access the `ORIGIN` constant directly on the class, without creating an instance: + +```java {usage}#accessStaticConstant + +``` + +## Mutable State + +The `Point` class we defined is **immutable**, meaning that once a `Point` is created, +its `x` and `y` values can never change, because the fields are `final`. +Immutability is generally a good thing: it makes classes easier to reason about, since you never have to worry about a value changing unexpectedly. + +Sometimes, however, we need a class whose state changes over time. +Let's define a `RobotTracker` class that tracks a robot's current position on the field. +The position starts somewhere and is updated as the robot moves, so it cannot be `final`: + +```java {tracker}#mutableField + +``` + +Notice that a field's type doesn't have to be a primitive type like `double`. +Just like `x` and `y` were `double` fields, `position` is a field whose type is our own `Point` class. +Any type, whether built into Java or one we defined ourselves, can be used as a field type. + +We still use `private` to prevent other classes from directly modifying the field, +so the only way to change the position is through the methods we define. +This matters because `move`, `distanceTo`, and `reset` can rely on `position` always being valid, +without worrying about another class setting it to something unexpected; +if outside code could reach in and overwrite `position` directly, `RobotTracker` couldn't guarantee its own behavior. + +The constructor works the same as before, initializing `position` to a given starting value: + +```java {tracker}#robotTrackerConstructor + +``` + +A class can have more than one constructor, as long as each one takes a different set of parameters. +This is useful when there's a sensible default: here, a `RobotTracker` with no arguments starts at the origin. +Instead of repeating `this.position = Point.ORIGIN`, we use `this(...)` to call the other constructor: + +```java {tracker}#constructorChaining + +``` + +When calling `move`, you pass in a `delta`, the change in position, and the method adds it to the current position. +This is mutation, as the method reassigns `this.position`, changing the object's state: + +```java {tracker}#stateMutation + +``` + +Notice how the `move` method has a return type of `void`. +This is because the method doesn't need to actually return anything. +It instead changes its own internal state. + +`distanceTo` computes the straight-line distance from the current position to a target. +It uses a local variable `diff` to hold the vector between the two points before taking its length: + +```java {tracker}#localVariableChain + +``` + +Because `position` is private, we also need a getter method to read the robot's current position: + +```java {tracker}#getPosition + +``` + +`reset` sets the position back to the origin. +Instead of writing `new Point(0, 0)`, we reuse the `Point.ORIGIN` constant. +Both would work, since a new `Point(0, 0)` has the same coordinates as `Point.ORIGIN`, +but reusing `ORIGIN` avoids creating a redundant object and makes the code's intent clearer: +we specifically mean _the_ origin, not just some point that happens to be at (0, 0): + +```java {tracker}#useStaticConstant + +``` + +Here is the complete `RobotTracker` class: + +```java {tracker}#robotTrackerFull + +``` + +And an example of using it: + +```java {usage}#trackerUsage + +``` + + + +## Classes and Methods Exercise + +