Skip to content

Instantly share code, notes, and snippets.

@Gnumaru
Created October 29, 2025 13:27
Show Gist options
  • Save Gnumaru/b337c3fa36ea6438673d07df913839b1 to your computer and use it in GitHub Desktop.
Save Gnumaru/b337c3fa36ea6438673d07df913839b1 to your computer and use it in GitHub Desktop.
how to simulate in gdscript a class property that can be asigned only the first time, like a 'final' property in java or a 'readonly' property in C#
# how to simulate a class property that can be asigned only the first time, like a 'final' property in java or a 'readonly' property in C#
#class_name ReadOnly
extends Node
# READONLY NON OBJECT WITH SPECIFIC TYPE
var readonly_int:int:
set(p):
if readonly_int != 0:printerr('cannot set readonly property "readonly_int" a second time'); return
readonly_int = p
# READONLY VARIANT WITHOUT SPECIFIC TYPE
var readonly_variant:Variant:
set(p):
if typeof(readonly_variant) != TYPE_NIL: printerr('cannot set readonly property "readonly_variant" a second time'); return
readonly_variant = p
# READONLY OBJECT WITH HELPER READONLY ID INT
var _readonly_object_id:int:
set(p):
if _readonly_object_id != 0: printerr('cannot set readonly property "_readonly_object_id" a second time'); return
_readonly_object_id = p
var readonly_object:Object:
set(p):
if _readonly_object_id != 0: printerr('cannot set readonly property "readonly_object" a second time'); return
readonly_object = p # assigns the object, regardless if it is null or not
# assigns the object id. uses -1 for null
if is_instance_valid(p): _readonly_object_id = p.get_instance_id()
else: _readonly_object_id = -1
# READONLY OBJECT WITH HELPER READONLY ID INT
var _readonly_object_b_id:int:
set(p):
if _readonly_object_b_id != 0: printerr('cannot set readonly property "_readonly_object_b_id" a second time'); return
_readonly_object_b_id = p
var readonly_object_b:Object:
set(p):
if _readonly_object_b_id != 0: printerr('cannot set readonly property "readonly_object_b" a second time'); return
readonly_object_b = p # assigns the object, regardless if it is null or not
# assigns the object id. uses -1 for null
if is_instance_valid(p): _readonly_object_b_id = p.get_instance_id()
else: _readonly_object_b_id = -1
func _ready()->void:
print('\n')
print(readonly_int) # print '0'
readonly_int = 12 # does not print anything
print(readonly_int) # print '12'
readonly_int = 8 # print 'cannot set readonly property "readonly_int"'
print(readonly_int) # print '12'
print('\n')
print(readonly_variant) # prints '<null>'
readonly_variant = 'my_string' # does not print anything
print(readonly_variant) # prints 'my_string'
readonly_variant = 8 # print 'cannot set readonly property "readonly_variant"'
print(readonly_variant) # prints 'my_string'
print('\n')
print(readonly_object) # prints '<null>'
readonly_object = Object.new() # does not print anything
print(readonly_object) # prints something like '<Object#26088572288>'
readonly_object = null # print 'cannot set readonly property "readonly_object"'
print(readonly_object) # prints '<Object#26088572288>'
readonly_object.free()
print(readonly_object) # prints '<Freed Object>'. the object was freed but the property will still be unsetable
readonly_object = null # print 'cannot set readonly property "readonly_object"'
print(readonly_object) # prints '<Freed Object>'
await get_tree().process_frame
print(readonly_object) # prints '<Freed Object>'. I dont' know when godot will efectively recollect the freed object memory.
print('\n')
print(readonly_object_b) # prints '<null>'
readonly_object_b = null # does not print anything, but this counts as the first set of the property, so it will be unsetable after this
print(readonly_object_b) # prints '<null>'
readonly_object_b = null # print 'cannot set readonly property "readonly_object_b'
print(readonly_object_b) # prints '<null>'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment