diff --git a/examples/stage0/snippets/classes-methods/Point.java b/examples/stage0/snippets/classes-methods/Point.java new file mode 100644 index 00000000..a313986b --- /dev/null +++ b/examples/stage0/snippets/classes-methods/Point.java @@ -0,0 +1,46 @@ +/* + * Copyright 2026 FRCSoftware + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// [pointFull] +class Point { + // [finalFields] + private final double x; + private final double y; + // [/finalFields] + // [staticConstant] + public static final Point ORIGIN = new Point(0, 0); + // [/staticConstant] + + // [constructor] + public Point(double x, double y) { + this.x = x; + this.y = y; + } + // [/constructor] + + // [getters] + public double getX() { return this.x; } + public double getY() { return this.y; } + // [/getters] + + // [plus] + public Point plus(Point other) { + return new Point(this.x + other.x, this.y + other.y); + } + // [/plus] + + public Point minus(Point other) { + return new Point(this.x - other.x, this.y - other.y); + } + + // [norm] + 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 00000000..fb883120 --- /dev/null +++ b/examples/stage0/snippets/classes-methods/RobotTracker.java @@ -0,0 +1,42 @@ +/* + * Copyright 2026 FRCSoftware + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// [robotTrackerFull] +class RobotTracker { + // [mutableField] + private Point position; + // [/mutableField] + + // [robotTrackerConstructor] + public RobotTracker(Point startPosition) { + this.position = startPosition; + } + // [/robotTrackerConstructor] + + // [stateMutation] + public void move(Point delta) { + this.position = this.position.plus(delta); + } + // [/stateMutation] + + // [localVariableChain] + public double distanceTo(Point target) { + Point diff = target.minus(this.position); + return diff.norm(); + } + // [/localVariableChain] + + public Point getPosition() { + return this.position; + } + + // [useStaticConstant] + 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 00000000..43d7d6ae --- /dev/null +++ b/examples/stage0/snippets/classes-methods/Usage.java @@ -0,0 +1,31 @@ +/* + * 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 + System.out.println(a.equals(b)); // false + // [/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 400bb706..11389009 100644 --- a/src/config/sidebarConfig.ts +++ b/src/config/sidebarConfig.ts @@ -78,10 +78,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 00000000..2ed6f283 --- /dev/null +++ b/src/content/docs/learning-course/stage0/classes-methods.mdx @@ -0,0 +1,268 @@ +--- +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 +--- + +import Aside from '@components/Aside.astro'; + +In an [earlier lesson](../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 `char`. +**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, such as WPILib, +but you can also define your own object types. + +Classes are a way to define object types. +A class is a template for creating objects, +with fields and methods that define the object's state and behavior. +An object is an instance of a class. + + + +## 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. + +In Java, every class is defined in a `.java` file, and starts with the `public class` keywords. +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. +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. +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. + +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. +They can also return a value, which is the result of the method's computation. +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. +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 constructor is a special method that is called when an object is created. +The constructor is a special method 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`. + +Next we're going to define some getter methods. +Getter methods are methods that simply 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 + +``` + +In this method, we use the `new` keyword to create a new `Point` object. +Because the constructor we defined earlier takes two parameters, +we then pass `this.x + other.x` and `this.y + other.y` as the parameters to the constructor, +so the new `Point` object will have the sum of the two `Point` objects' coordinates. + +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 (`.`). + +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 + +``` + +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. + +The constructor works the same as before, initializing `position` to a given starting value: + +```java {tracker}#robotTrackerConstructor + +``` + +The `move` method accepts a `delta`, the change in position, and adds it to the current position. +This is mutation, as the method reassigns `this.position`, changing the object's state: + +```java {tracker}#stateMutation + +``` + +`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 + +``` + +`reset` sets the position back to the origin. +Instead of writing `new Point(0, 0)`, we reuse the `Point.ORIGIN` constant: + +```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 + +