See all glossary terms

Inner Classes

Godot has two ways of creating new classes:
  1. Create a new script. Each script is a class (optionally named with the class_name keyword).
  2. Use the class keyword in a script. It creates an inner class.
Here's an example with two inner classes:
class Person:
	var name := ""
	var age := 0

class Superhero extends Person:
	var power := ""
Inner classes have a limitation compared to creating scripts: you cannot instantiate them from the editor, so you can't use them for nodes and resources that you'd like to interact with in the editor. You can only use them for data containers and helper classes.
But inner classes offer at least two benefits:
  1. They allow you to keep related classes grouped in a single file. It makes it easy to read and maintain related code.
  2. Their names are scoped locally, which helps to hide them from the rest of the project.
In this example, I define an inner class with a leading underscore, _Power to share code between the Flight and Fire classes and hide it from the outside world:
class_name Powers:

class _Power:
	var name := ""

class Flight extends _Power:
	var duration := 10

class Fire extends _Power:
	var strength := 5
With a script like this, anywhere in the project, you can call Powers.Flight.new() to create a new instance of Flight.
Grouping related classes together like this is something we call "namespacing". It compartmentalizes the code and helps to avoid naming conflicts. Imagine that you want a power named Fire and that you also need to define the magical element Fire in your game.
Because the Fire power is an inner class, you access it as Powers.Fire, and you can have two inner classes named Fire in the same project without conflicts.

Using inner classes for strongly typed data containers

Another feature of inner classes is to create well-typed objects to store data. Suppose I have a complicated function to get user properties for a web server connection. I could return a dictionary:
func get_connect_properties() -> Dictionary:
	return {
		"user_name": "AzureDiamond"
		"password": "hunter2"
		"server_ip": "localhost"
		"port": 3000,
		"role": "moderator"
	}
But then to know what keys are available, I need to look at the function's documentation or the source code. If I try to access a nonexistent key, I won't get any error until the game runs. It's a common source of bugs.
Instead, you can use an inner class to define the data structure:
class ConnectionProperties:
	user_name: String
	password: String
	server_ip: String
	port: int
	role: String

func get_connect_properties() -> ConnectionProperties:
	var properties := ConnectionProperties.new()
	properties.user_name = "AzureDiamond"
	properties.password = "hunter2"
	properties.server_ip = "localhost"
	properties.port = 3000
	properties.role = "moderator"
	return properties
Now, when someone uses get_connect_properties(), they get a well-typed object with autocompletion and error reporting in the editor.

See Also

Related terms in the Glossary