Methods: reusable actions
Package code into named actions with inputs and outputs.
A method is a named action: a block of code with inputs and an output that you can run whenever you need it. On this page you will write your own methods, learn how they take inputs and give back results, and see how a plugin is really a set of methods that Paper calls and methods you write to help them.
You already use methods
Every time you wrote IO.println("..."), you used a method. So are player.sendRichMessage(...), player.getHealth() and Math.max(7, 12). Somebody else wrote the code inside them; you just use them by name. Using a method is called calling it.
Now you will write your own. Methods solve three problems that every plugin has:
- Repetition. If three commands all need "work out the player's max health", write it once and call it three times.
- Readability.
if (isValidName(nickname))explains itself. Fifteen lines of character checks do not. - Small pieces. A big feature becomes a few small methods you can understand, test and fix one at a time.
Defining and calling a method
Here is a program with a method of its own. Run it:
1void main() {2 announceRestart();3 IO.println("...players keep playing...");4 announceRestart();5}6 7void announceRestart() {8 IO.println("[Server] Restart in 5 minutes!");9 IO.println("[Server] Find a safe place to log out.");10}- A
call : the method's name and round brackets. Java jumps into the method, runs all of its lines, then comes back here and carries on. - Normal code between the two calls.
- The method's definition.
voidmeans it gives nothing back,announceRestartis its name, and the empty()means it needs no inputs. - The method's
body : the lines between its braces. They run every time someone calls the method.
The two lines inside announceRestart run twice, because the method is called twice. If you want to change the message, you change it in one place, and both calls get the new version.
Parameters: giving a method inputs
A method that always does exactly the same thing is useful, but most methods need some information first: which player, how much damage, which name. Those inputs are called parameters:
1void main() {2 greet("Steve", 30);3 greet("Alex", 5);4}5 6void greet(String name, int level) {7 IO.println("Welcome, " + name + "!");8 if (level >= 30) {9 IO.println("Level " + level + ": you can use the enchanting arena.");10 } else {11 IO.println("Level " + level + ": reach level 30 to unlock the arena.");12 }13}- Calls
greetwith two values:"Steve"and30. The values you pass in a call are calledarguments . - Two parameters, separated by a comma. Each one has a type and a name, like a variable. When Steve's call runs,
nameis"Steve"andlevelis30. - Parameters work like normal variables inside the method, so you can use them in conditions, math and text.
The rule: a call must pass the same number of arguments, of the right types, in the same order as the parameters. Steve goes into name because he comes first. Forget an argument, and Java refuses to compile:
1void main() {2 greet("Steve");3}4 5void greet(String name, int level) {6 IO.println("Welcome, " + name + " (level " + level + ")");7}- Only one argument, but
greetneeds two.
Read the error from the bottom up: "required: String,int" is what the method wants, "found: String" is what you gave it. You will see this exact error with Paper methods too, for example when you forget an argument to registerEvents.
Returning a value
Many methods work something out and hand back the answer. The answer is the return value. Instead of void, you write the type of the answer before the method's name, and use return to hand it back:
1void main() {2 double heal = healAmount(6.0, 20.0, 50);3 IO.println("Healing 6.0 health by 50%: +" + heal);4 5 double newHealth = 17.0 + healAmount(17.0, 20.0, 50);6 IO.println("Healing 17.0 health by 50% gives " + newHealth);7}8 9double healAmount(double health, double maxHealth, double percent) {10 double amount = maxHealth * percent / 100.0;11 double missing = maxHealth - health;12 return Math.min(amount, missing);13}- This method returns a
double: the amount of health to add. It needs three inputs to work that out. - 50% of 20 max health is 10.
- But a player at 17 health is only missing 3. Healing 10 would go over the maximum.
returnhands the result back to whoever called the method, and ends the method right there. The smaller of the two numbers is the safe heal.- The call is replaced by the returned value, which is stored in
heal. - A call that returns a value can be used anywhere a value fits, even in the middle of math.
Returning is not printing
Beginners often print a result inside a method when they should return it. Printing shows text to a human; returning gives a value to your code. Only a returned value can be used again:
1void main() {2 printTotal(30, 12);3 4 int total = total(30, 12);5 IO.println("Twice the total is " + (total * 2));6}7 8void printTotal(int coins, int bonus) {9 IO.println("Total: " + (coins + bonus));10}11 12int total(int coins, int bonus) {13 return coins + bonus;14}- Prints the total and gives nothing back. The rest of your program cannot use that number.
- Returns the total. Now the caller decides what to do with it: print it, double it, save it.
- Only possible because
total(...)returned a number.
In plugins this matters a lot. A method that returns a value can feed a chat message, an action bar, a scoreboard and a config file. A method that prints to the console can only ever print to the console.
Several returns: deciding inside a method
return ends the method immediately, so you can return from several places. Combined with the guard clauses from Making decisions, this gives very clean decision methods:
1void main() {2 IO.println("2.5 -> " + healthLabel(2.5));3 IO.println("8.0 -> " + healthLabel(8.0));4 IO.println("20.0 -> " + healthLabel(20.0));5}6 7String healthLabel(double health) {8 if (health < 4) {9 return "Critical";10 }11 if (health < 10) {12 return "Hurt";13 }14 return "Healthy";15}- Returns a
String: a word that describes the health value. - Below 4, the answer is known, so the method stops here. No
elseneeded. - The last line runs only when no earlier
returndid. Java checks that every path through the method returns something; leave this line out and you get "missing return statement".
A useful one: formatting time
Cooldowns, countdowns and play time all need to turn a number of seconds into something readable like 2:05. That is a perfect job for a method:
1void main() {2 IO.println(formatTime(5));3 IO.println(formatTime(65));4 IO.println(formatTime(600));5 IO.println("Next event in " + formatTime(125) + ".");6}7 8String formatTime(int totalSeconds) {9 int minutes = totalSeconds / 60;10 int seconds = totalSeconds % 60;11 return String.format("%d:%02d", minutes, seconds);12}- One input (a number of seconds), one output (text).
- Whole minutes. Integer division drops the remainder: 125 / 60 is 2.
- The seconds left over: 125 % 60 is 5.
- Builds the text.
%02dpads the seconds to two digits, so 5 becomes05. Working with text explains format codes. - The returned text can be glued into a bigger message.
Methods that call methods
Methods can call other methods, so you can build big ideas from small ones. Minecraft names must be 3 to 16 characters long and use only letters, digits and underscores. Checking that is one method that uses a second, smaller method:
1void main() {2 String[] names = {"Steve", "xX_Pro_Xx", "Al", "Notch!", "ThisNameIsWayTooLong"};3 for (String name : names) {4 IO.println(name + " -> " + isValidName(name));5 }6}7 8boolean isValidName(String name) {9 if (name.length() < 3 || name.length() > 16) {10 return false;11 }12 for (int i = 0; i < name.length(); i++) {13 if (!isAllowedCharacter(name.charAt(i))) {14 return false;15 }16 }17 return true;18}19 20boolean isAllowedCharacter(char c) {21 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';22}- A for-each loop from the last chapter tests five names with one call each.
- Returns
trueorfalse. Methods that answer a yes-or-no question usually start withis,hasorcan. - Too short or too long? Then the answer is already known: return
false. - The character at position
i. The loop checks every character of the name. - A method calling another method. If any character is not allowed, the whole name is invalid, and the method returns at once.
- Only reached when the length was fine and every character passed.
- A tiny helper with one job. A
charis a single character, written in single quotes like'a'. - You can return a condition directly. Its true or false value is the answer, so no if is needed.
In a plugin: helper methods
Real plugin classes are full of small helper methods like these. Here is a /heal command that heals half of a player's missing health. It uses the exact healAmount method from above, plus a helper that finds the player's maximum health:
Where this file livesjava-methods-demosrcmainjavacomexamplejavamethodsdemoHealCommand.java
The package com.example.javamethodsdemo is the folder path com/example/javamethodsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- java-methods-demo/
- src/main/
- java/com/example/javamethodsdemo/Package com.example.javamethodsdemo
- HealCommand.javayou are hereCommand (BasicCommand)
- MethodsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- NickCommand.javaCommand (BasicCommand)
- SessionCommand.javaCommand (BasicCommand)
- TimeFormat.javaHelper class
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/javamethodsdemo/Package com.example.javamethodsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.javamethodsdemo;2 3import io.papermc.paper.command.brigadier.BasicCommand;4import io.papermc.paper.command.brigadier.CommandSourceStack;5import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;6import org.bukkit.attribute.Attribute;7import org.bukkit.attribute.AttributeInstance;8import org.bukkit.entity.Player;9 10public final class HealCommand implements BasicCommand {11 12 private static final double HEAL_PERCENT = 50;13 14 @Override15 public void execute(CommandSourceStack source, String[] args) {16 if (!(source.getExecutor() instanceof Player player)) {17 source.getSender().sendRichMessage("<red>Only players can be healed.");18 return;19 }20 21 double maxHealth = maxHealthOf(player);22 double amount = healAmount(player.getHealth(), maxHealth, HEAL_PERCENT);23 if (amount <= 0) {24 player.sendRichMessage("<yellow>You are already at full health.");25 return;26 }27 28 player.setHealth(player.getHealth() + amount);29 player.sendRichMessage("<green>Healed <amount> hearts.",30 Placeholder.unparsed("amount", String.valueOf(amount / 2)));31 }32 33 @Override34 public String permission() {35 return "methodsdemo.heal";36 }37 38 private static double maxHealthOf(Player player) {39 AttributeInstance maxHealth = player.getAttribute(Attribute.MAX_HEALTH);40 if (maxHealth == null) {41 return 20.0;42 }43 return maxHealth.getValue();44 }45 46 private static double healAmount(double health, double maxHealth, double percent) {47 double amount = maxHealth * percent / 100.0;48 double missing = maxHealth - health;49 return Math.min(amount, missing);50 }51}- A class is a container for methods (and data). This one holds the command's methods. Classes and objects covers classes properly.
- A constant: a named value that never changes. Easy to find and tweak.
- Paper calls this method when a player types
/heal. You never call it yourself. - Calls our first helper. The long attribute lookup now hides behind a clear name.
- Calls our second helper with three arguments: the current health, the maximum and the percent.
- At full health there is nothing missing, so the amount is 0.
- Paper's method that changes the player's health. Thanks to
healAmount, the new value never goes above the maximum. - Health is counted in half hearts, so 10 health is 5 hearts.
- Another method Paper calls: it asks which permission is needed to use the command.
- A helper that takes a
Playeras its input.privatemeans only this class can call it;staticis explained in a moment. - The player's maximum health setting. Paper may return
nullfor it, so the next lines fall back to the normal 20. - The same method as in the runnable example, copied unchanged. Plain Java logic works the same inside a plugin.
You are already at full health.
The /nick command uses isValidName and isAllowedCharacter from the runnable example to check a nickname before using it:
Where this file livesjava-methods-demosrcmainjavacomexamplejavamethodsdemoNickCommand.java
The package com.example.javamethodsdemo is the folder path com/example/javamethodsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- java-methods-demo/
- src/main/
- java/com/example/javamethodsdemo/Package com.example.javamethodsdemo
- HealCommand.javaCommand (BasicCommand)
- MethodsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- NickCommand.javayou are hereCommand (BasicCommand)
- SessionCommand.javaCommand (BasicCommand)
- TimeFormat.javaHelper class
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/javamethodsdemo/Package com.example.javamethodsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
11@Override12public void execute(CommandSourceStack source, String[] args) {13 if (!(source.getExecutor() instanceof Player player)) {14 source.getSender().sendRichMessage("<red>Only players can have a nickname.");15 return;16 }17 if (args.length != 1) {18 player.sendRichMessage("<red>Usage: /nick NewName");19 return;20 }21 22 String nickname = args[0];23 if (!isValidName(nickname)) {24 player.sendRichMessage("<red>Nicknames need 3 to 16 letters, digits or underscores.");25 return;26 }27 28 player.displayName(Component.text(nickname));29 player.sendRichMessage("<green>Your nickname is now <white><nick></white>.",30 Placeholder.unparsed("nick", nickname));31}- The player must type exactly one word after
/nick. - One readable line. All the length and character rules live in the helper method further down the file.
- Sets the name shown for this player in chat. It is reset when the player rejoins; saving it is a job for Saving player data.
Your nickname is now Builder_Bob.
Scope: where variables live
A variable exists only inside the block where it was created. This is called its scope. Each method is its own block, so each method has its own private set of variables. Even when two methods use the same variable name, they are two different variables:
1void main() {2 int coins = 100;3 4 addBonus(coins);5 IO.println("After addBonus, coins is still " + coins);6 7 coins = withBonus(coins);8 IO.println("After withBonus, coins is " + coins);9}10 11void addBonus(int coins) {12 coins = coins + 50;13 IO.println("Inside addBonus, its own coins is " + coins);14}15 16int withBonus(int coins) {17 return coins + 50;18}- Passes the value 100 into
addBonus, not the variable itself. - This
coinsis a brand-new variable that belongs toaddBonus. It starts as a copy of 100. - Changes only the copy. The copy disappears when the method ends.
- Proof:
main'scoinswas never touched. - The fix: return the new value and store it. This is how a method gives a result back to its caller.
The same rule means you cannot reach into another method's variables:
1void main() {2 giveReward();3 IO.println("You got " + reward + " coins");4}5 6void giveReward() {7 int reward = 50;8 IO.println("Reward ready: " + reward);9}- Fails:
rewardonly exists insidegiveReward. Tomain, it does not exist at all.
"cannot find symbol" means "I do not know any variable or method with this name here". When you see it, check the spelling first, then check whether the variable was created inside a different block or method. To share a value, return it, or pass it in as an argument. (Classes add a third way, fields, in Classes and objects.)
Overloading: same name, different inputs
Two methods can have the same name if their parameters are different. This is called overloading. Java picks the right one by looking at the arguments in each call:
1void main() {2 IO.println(formatCoins(1));3 IO.println(formatCoins(250));4 IO.println(formatCoins(3, "emerald"));5}6 7String formatCoins(int amount) {8 return formatCoins(amount, "coin");9}10 11String formatCoins(int amount, String currency) {12 if (amount == 1) {13 return amount + " " + currency;14 }15 return amount + " " + currency + "s";16}- Version 1: just an amount. It calls version 2 with the default currency, so the real logic lives in one place.
- Version 2: an amount and a currency name.
- Two arguments, so Java picks version 2.
The Paper API uses overloading everywhere. ItemStack.of(Material.DIAMOND) makes one diamond, ItemStack.of(Material.DIAMOND, 16) makes sixteen: same name, different parameters. sendRichMessage has one version with only the text and another that also takes placeholders. In IntelliJ, put the cursor inside the round brackets of a call and press Ctrl+P to list every version and its parameters.
static and instance methods
Look at these two calls:
1double health = player.getHealth();2int bigger = Math.max(7, 12);- Called on a specific player. Steve's health and Alex's health are different, so you must say whose health you want. This is an
instance method . - Called on the class name,
Math. It needs no particular object: the bigger of 7 and 12 is the same for everyone. This is astatic method .
An instance method belongs to one particular object (one player, one item, one world) and works with that object's data. A static method belongs to the class itself and only uses its parameters. You write static in front of a method to make it static.
The practice files on this site hide this detail, but most code online uses the classic full form, where main and its helpers are static:
1public class ClassicStatic {2 3 public static void main(String[] args) {4 System.out.println("Next event in " + formatTime(125));5 System.out.println("Bigger number: " + Math.max(7, 12));6 }7 8 static String formatTime(int totalSeconds) {9 int minutes = totalSeconds / 60;10 int seconds = totalSeconds % 60;11 return String.format("%d:%02d", minutes, seconds);12 }13}- The classic starting point you met in How code reads. It is
static, so Java can run it without creating an object first. - A static method can call other static methods in the same class by name.
- The same method as before, with
staticin front, because it only needs its parameter.
In plugins you will see both kinds. The helpers in HealCommand are private static because they only use their parameters. A small class full of static helpers is also common. This plugin keeps formatTime in its own TimeFormat class so every command can use it:
Where this file livesjava-methods-demosrcmainjavacomexamplejavamethodsdemoTimeFormat.java
The package com.example.javamethodsdemo is the folder path com/example/javamethodsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- java-methods-demo/
- src/main/
- java/com/example/javamethodsdemo/Package com.example.javamethodsdemo
- HealCommand.javaCommand (BasicCommand)
- MethodsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- NickCommand.javaCommand (BasicCommand)
- SessionCommand.javaCommand (BasicCommand)
- TimeFormat.javayou are hereHelper class
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/javamethodsdemo/Package com.example.javamethodsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.javamethodsdemo;2 3public final class TimeFormat {4 5 private TimeFormat() {6 }7 8 public static String formatTime(int totalSeconds) {9 int minutes = totalSeconds / 60;10 int seconds = totalSeconds % 60;11 return String.format("%d:%02d", minutes, seconds);12 }13}- Nobody needs a
TimeFormatobject, so this blocks anyone from making one. You will learn about these "constructors" in Classes and objects. - Same logic as the runnable version.
publiclets the other classes in the plugin call it.
Where this file livesjava-methods-demosrcmainjavacomexamplejavamethodsdemoSessionCommand.java
The package com.example.javamethodsdemo is the folder path com/example/javamethodsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- java-methods-demo/
- src/main/
- java/com/example/javamethodsdemo/Package com.example.javamethodsdemo
- HealCommand.javaCommand (BasicCommand)
- MethodsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- NickCommand.javaCommand (BasicCommand)
- SessionCommand.javayou are hereCommand (BasicCommand)
- TimeFormat.javaHelper class
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/javamethodsdemo/Package com.example.javamethodsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
10@Override11public void execute(CommandSourceStack source, String[] args) {12 if (!(source.getExecutor() instanceof Player player)) {13 source.getSender().sendRichMessage("<red>The console is always online.");14 return;15 }16 17 long onlineMillis = System.currentTimeMillis() - player.getLastLogin();18 int onlineSeconds = (int) (onlineMillis / 1000);19 player.sendRichMessage("<gray>You have been online for <white><time></white> (minutes:seconds).",20 Placeholder.unparsed("time", TimeFormat.formatTime(onlineSeconds)));21}- The current time in milliseconds. Subtracting the time the player logged in gives how long they have been online.
- Milliseconds to seconds, then turned into an
intbecause that is whatformatTimetakes. - A static method from another class: class name, dot, method name.
Do not worry if static still feels fuzzy. Classes and objects explains it fully once you know what an object is. For now: static methods are called on a class name, instance methods are called on an object.
Methods that Paper calls
Here is the big idea that connects this page to every plugin you will write: a plugin has no main method. Paper is the program that runs. Your plugin is a collection of methods that Paper calls at the right moments, plus helper methods that you call yourself. Look at the demo's main class:
Where this file livesjava-methods-demosrcmainjavacomexamplejavamethodsdemoMethodsDemoPlugin.java
The package com.example.javamethodsdemo is the folder path com/example/javamethodsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- java-methods-demo/
- src/main/
- java/com/example/javamethodsdemo/Package com.example.javamethodsdemo
- HealCommand.javaCommand (BasicCommand)
- MethodsDemoPlugin.javayou are hereMain class: Paper starts here (named as main in plugin.yml)
- NickCommand.javaCommand (BasicCommand)
- SessionCommand.javaCommand (BasicCommand)
- TimeFormat.javaHelper class
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/javamethodsdemo/Package com.example.javamethodsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.javamethodsdemo;2 3import io.papermc.paper.command.brigadier.Commands;4import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;5import org.bukkit.plugin.java.JavaPlugin;6 7public final class MethodsDemoPlugin extends JavaPlugin {8 9 @Override10 public void onEnable() {11 getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> {12 Commands commands = event.registrar();13 commands.register("heal", "Heal half of your missing health", new HealCommand());14 commands.register("session", "Show how long you have been online", new SessionCommand());15 commands.register("nick", "Change the name shown in chat", new NickCommand());16 });17 getLogger().info("Commands /heal, /session and /nick are ready.");18 }19 20 @Override21 public void onDisable() {22 getLogger().info("Goodbye!");23 }24}- Says "this method replaces one that Paper already knows about". If you misspell the name, the compiler warns you instead of Paper silently never calling it. Inheritance and interfaces explains the details.
- Paper calls this once when the plugin starts. You never call
onEnable()yourself. - Hands Paper an object of your command class, so Paper can call its
executemethod later. - Paper calls this once when the server stops. It is the place to save data.
| Method | Who calls it | When |
|---|---|---|
onEnable() | Paper | Once, when your plugin starts |
onDisable() | Paper | Once, when your plugin stops |
@EventHandler methods | Paper | Every time their event happens (see Events and listeners) |
execute(...) in a command | Paper | Every time someone runs the command (see Commands, part 1) |
Your helpers (healAmount, isValidName) | You | Whenever your code calls them |
Paper's methods (player.setHealth, sendRichMessage) | You | Whenever your code calls them |
For the methods Paper calls, the rules are strict: the name and parameters must be exactly what Paper expects (onEnable, execute(CommandSourceStack source, String[] args)), or for event handlers, the one parameter must be an event type. For your own helpers, you choose everything.
- java-methods-demo/
- src/
- main/
- java/
- com/example/javamethodsdemo/
- MethodsDemoPlugin.javaMain class: onEnable registers the commands, onDisable says goodbye
- HealCommand.java/heal with the maxHealthOf and healAmount helpers
- SessionCommand.java/session, uses TimeFormat.formatTime
- NickCommand.java/nick with the isValidName and isAllowedCharacter helpers
- TimeFormat.javaA small class with one static helper method
- com/example/javamethodsdemo/
- resources/
- plugin.ymlName, main class and permissions
- java/
- main/
- src/
[14:02:11 INFO]: [MethodsDemo] Enabling MethodsDemo v1.0.0[14:02:11 INFO]: [MethodsDemo] Commands /heal, /session and /nick are ready.[14:40:57 INFO]: [MethodsDemo] Disabling MethodsDemo v1.0.0[14:40:57 INFO]: [MethodsDemo] Goodbye!Download the demo plugin to try the three commands on your test server, or open it in the Compile Lab.
Good names and small methods
The computer does not care what you call a method. People do, including you in three months. These habits make plugin code much easier to read:
- Name actions with verbs:
giveStarterKit,healPlayer,formatTime. Notkit,doStufformethod2. - Name yes-or-no questions with is, has or can:
isValidName,hasCooldown,canAfford. Thenif (canAfford(coins, price))reads like English. - Give each method one job. If you need "and" to describe it ("checks the name and saves it and sends a message"), it probably wants to be two or three methods.
- Keep methods short. If a method no longer fits on your screen, look for a piece you can move into a helper with a good name.
- Copied code is a method waiting to happen. If you paste the same five lines into a second place, make a method and call it twice.
Mistakes to avoid
- Forgetting the brackets.
announceRestart;is not a call. A call always has round brackets:announceRestart();. - Missing return. A method with a return type must return a value on every path. Java says "missing return statement" when one path has none.
- Ignoring a returned value.
name.toUpperCase();on its own line does nothing useful: the uppercase text is returned and thrown away. Store it:name = name.toUpperCase(Locale.ROOT);. - Arguments in the wrong order.
healAmount(maxHealth, health, 50)compiles, because both aredouble, but gives wrong answers. In IntelliJ, the gray parameter name hints in front of each argument help you spot this. - Expecting a method to change your variable. Parameters are copies. Return the new value instead.
- Writing a method inside another method. Every method sits directly inside the class (or next to
mainin practice files).
Titles and prices as methods
In Making decisions you picked a title for a number of kills. Now turn that into a reusable method, so a loop can call it for many players. Finish the two methods in the starter:
titleFor(int kills)returns Peaceful (0), Fighter (1 to 9), Warrior (10 to 49), Champion (50 to 99) or Legend (100 or more).canAfford(int coins, int price)returns whether the player has enough coins.
1void main() {2 int[] killCounts = {0, 5, 27, 50, 250};3 for (int kills : killCounts) {4 IO.println(kills + " kills: " + titleFor(kills));5 }6 7 IO.println("Can afford a 50 coin kit with 35 coins? " + canAfford(35, 50));8 IO.println("Can afford a 50 coin kit with 80 coins? " + canAfford(80, 50));9}10 11String titleFor(int kills) {12 return "Unknown";13}14 15boolean canAfford(int coins, int price) {16 return false;17}- A placeholder so the starter compiles. Replace it with real logic.
Hint 1
Use early returns in titleFor: if (kills == 0) { return "Peaceful"; }, then the next check, and end with return "Legend";.
Hint 2
canAfford fits in one line: return the comparison itself.
Show the solution
Each check returns as soon as it knows the answer, so no else is needed. canAfford returns the boolean result of the comparison directly.
1void main() {2 int[] killCounts = {0, 5, 27, 50, 250};3 for (int kills : killCounts) {4 IO.println(kills + " kills: " + titleFor(kills));5 }6 7 IO.println("Can afford a 50 coin kit with 35 coins? " + canAfford(35, 50));8 IO.println("Can afford a 50 coin kit with 80 coins? " + canAfford(80, 50));9}10 11String titleFor(int kills) {12 if (kills == 0) {13 return "Peaceful";14 }15 if (kills < 10) {16 return "Fighter";17 }18 if (kills < 50) {19 return "Warrior";20 }21 if (kills < 100) {22 return "Champion";23 }24 return "Legend";25}26 27boolean canAfford(int coins, int price) {28 return coins >= price;29}- Only reached when kills is not 0, so this covers 1 to 9.
- The fallback for 100 and more.
- The comparison is already true or false, which is exactly the answer.
One counting method, three items
In Loops, the /diamonds command counted one kind of item. Write a /valuables command that tells players how many diamonds, emeralds and gold ingots they carry, in one message:
Do not copy the counting loop three times. Write it once, as a method.
Hint 1
The method needs two inputs, the player and the item type, and returns a number: private static int countItems(Player player, Material material).
Hint 2
Inside, use the loop from the diamond counter, but compare with material instead of Material.DIAMOND, and finish with return total;.
Hint 3
In execute, call it three times: int diamonds = countItems(player, Material.DIAMOND); and so on.
Show the solution
The material parameter is what makes the method reusable: the same loop counts any item. Adding a fourth item later is one more line in execute.
Where this file livesjava-methods-exercisesrcmainjavacomexamplejavamethodsexerciseValuablesCommand.java
The package com.example.javamethodsexercise is the folder path com/example/javamethodsexercise inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- java-methods-exercise/
- src/main/
- java/com/example/javamethodsexercise/Package com.example.javamethodsexercise
- MethodsExercisePlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- ValuablesCommand.javayou are hereCommand (BasicCommand)
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/javamethodsexercise/Package com.example.javamethodsexercise
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.javamethodsexercise;2 3import io.papermc.paper.command.brigadier.BasicCommand;4import io.papermc.paper.command.brigadier.CommandSourceStack;5import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;6import org.bukkit.Material;7import org.bukkit.entity.Player;8import org.bukkit.inventory.ItemStack;9 10public final class ValuablesCommand implements BasicCommand {11 12 @Override13 public void execute(CommandSourceStack source, String[] args) {14 if (!(source.getExecutor() instanceof Player player)) {15 source.getSender().sendRichMessage("<red>Only players have an inventory to count.");16 return;17 }18 19 int diamonds = countItems(player, Material.DIAMOND);20 int emeralds = countItems(player, Material.EMERALD);21 int gold = countItems(player, Material.GOLD_INGOT);22 23 player.sendRichMessage("<aqua><diamonds> diamonds</aqua>, <green><emeralds> emeralds</green>, <gold><ingots> gold ingots</gold>",24 Placeholder.unparsed("diamonds", String.valueOf(diamonds)),25 Placeholder.unparsed("emeralds", String.valueOf(emeralds)),26 Placeholder.unparsed("ingots", String.valueOf(gold)));27 }28 29 private static int countItems(Player player, Material material) {30 int total = 0;31 for (ItemStack item : player.getInventory().getContents()) {32 if (item != null && item.getType() == material) {33 total += item.getAmount();34 }35 }36 return total;37 }38}- Three calls to one method, each with a different argument.
- Two parameters in, one
intout. - Counts the slot only if it holds the requested item.
!= nullcomes first so empty slots are skipped safely. - Hands the count back to
execute.
The finished exercise plugin includes the main class that registers /valuables.
Recap
- A method is a named block of code. Define it once, call it by name with round brackets as often as you like.
- Parameters are a method's inputs; the values you pass in a call are arguments. Number, types and order must match.
voidmethods return nothing. Other methods declare a return type and usereturnto hand back a value, which ends the method.- Return values can be reused anywhere; printed text cannot. Prefer returning.
- Variables only live in the block that created them. Parameters are copies.
- Overloaded methods share a name but take different parameters, like
ItemStack.of(material)andItemStack.of(material, amount). - Static methods are called on a class (
Math.max); instance methods on an object (player.getHealth()). - A plugin is methods Paper calls (
onEnable, event handlers,execute) plus helper methods you call yourself.
Quick quiz
What does
voidmean invoid announceRestart()?voidis the return type: "nothing comes back". The empty round brackets are what say "no parameters". A void method can still do plenty, like printing or sending messages.Given
int total(int coins, int bonus) { return coins + bonus; }, what istotal(30, 12) * 2?The call returns 42, and the returned value takes the place of the call, so the expression becomes42 * 2. Any call that returns a value can be used inside math or text.A method
void addBonus(int coins) { coins = coins + 50; }is called with a variable holding 100. What does the variable hold afterward?The parameter is a copy. Changing the copy does not change the caller's variable. To get the new value back, the method must return it and the caller must store it.Why can
ItemStack.of(Material.DIAMOND)andItemStack.of(Material.DIAMOND, 16)both exist?Overloading lets one name have several versions as long as their parameters differ. Java looks at the number and types of the arguments to decide which version to run.In a plugin, who calls your
onEnable()method?Plugins have nomainmethod. Paper runs the server and calls your plugin's methods at the right moments:onEnableat startup, event handlers when events happen,executewhen a command is typed.
Next steps
- Classes and objects: what
playerreally is, and why some methods are called on objects. - The main class and plugin lifecycle: everything Paper does around
onEnableandonDisable. - Practice Arena: write helper methods inside real plugin code and check them with tests.