ClassDefinition_step3 - gama-platform/gama GitHub Wiki

Inheritance

Declaring a Parent Class

Every class that does not declare an explicit parent extends the built-in root class object. Use parent: to declare inheritance:

class vehicle {
	string name <- "vehicle";
	float speed <- 0.0;
	float max_speed <- 120.0;
	float distance_km <- 0.0;
	
	float move() {
		distance_km <- distance_km + speed;
		return speed;
	}
	
	string summary() {
		return "[vehicle] " + name + "  speed=" + (speed with_precision 1);
	}
}

Child Class with parent:

A child class inherits all attributes and actions from its parent:

class car parent: vehicle {
	
	string name <- "car";                    // override parent default
	float fuel <- 50.0;                       // new attribute
	float consumption_per_km <- 0.08;         // new attribute
	
	// Override the parent's move() with custom logic
	float move() {
		if fuel <= 0.0 {
			speed <- 0.0;
			return 0.0;   // out of fuel — can't move
		}
		float d <- super.move();                       // parent handles distance
		fuel <- max(0.0, fuel - d * consumption_per_km);  // consume fuel
		return d;
	}
	
	// Override summary() to append fuel info
	string summary() {
		return super.summary() + "  fuel=" + (fuel with_precision 1) + "L";
	}
	
	action refuel(float capacity) {
		fuel <- capacity;
	}
}

Key Points

  • parent: ClassName makes the new class extend that class.
  • super.action(args) calls the parent's version of a typed action (one that returns a value).
  • invoke action(args) calls the parent's version of a void procedure (action with no return).
  • Redefining an action with the same signature overrides it (dynamic dispatch).

Second Child with Same Parent

Another child can extend the same parent with different behavior:

class bicycle parent: vehicle {
	
	string name <- "bicycle";
	float max_speed <- 30.0;                       // override parent max
	string rider <- "nobody";                       // new attribute
	
	// Does NOT override move() — the parent implementation is used as-is.
	// Only overrides summary().
	string summary() {
		return super.summary() + "  rider=" + rider;
	}
}

Multi-Level Inheritance

A child can extend another child, creating a hierarchy:

class sports_car parent: car {     // car extends vehicle — so sports_car extends both
	
	string name <- "sports_car";
	float max_speed <- 250.0;         // override car's max_speed
	bool turbo_active <- false;
	float boost_factor <- 1.5;
	
	// Override car's move() (which itself calls vehicle.move())
	float move() {
		if turbo_active {
			float normal_speed <- speed;
			speed <- min(speed * boost_factor, max_speed);
			float d <- super.move();   // car.move() checks fuel → calls vehicle.move()
			speed <- normal_speed;      // restore
			return d;
		}
		return super.move();
	}
	
	// Override car's summary() (calling super.summary() which already includes vehicle info)
	string summary() {
		return super.summary() + "  turbo=" + turbo_active;
	}
}

The call chain for sports_car.move() when turbo is active:

  1. sports_car.move() → calls super.move()
  2. car.move() → calls super.move()
  3. vehicle.move() → the base implementation that updates distance

Runtime Type Testing with is

The is operator tests whether an object is an instance of a class or any of its subclasses:

car c <- car();
sports_car sc <- sports_car();
bicycle b <- bicycle();

c  is car;         // true
c  is vehicle;     // true (parent)
sc is sports_car;  // true
sc is car;         // true (sc extends car, which extends vehicle)
sc is vehicle;     // true (transitive)
b  is car;         // false (sibling — both extend vehicle but are unrelated)

Polymorphic Lists

An object typed as the parent class can hold any subtype object:

list<vehicle> fleet <- [c, b, sc];

loop v over: fleet {
	v.move();            // dispatches to the actual object's overridden move()
	v.accelerate(20.0);  // shared inherited action — same for all
	write v.summary();   // dispatches to each class's overridden summary()
}

Selecting Objects by Type

Use the select keyword with is to filter:

list<vehicle> fleet <- [c, b, sc];

// Get only cars (and subclasses like sports_car)
list<vehicle> only_cars <- fleet select (each is car);
write only_cars collect each.name;   // ["sedan", "gt500"]

Example: Full Model

class vehicle {
	string name <- "vehicle";
	float speed <- 0.0;
	float max_speed <- 120.0;
	float distance_km <- 0.0;
	
	float move() {
		distance_km <- distance_km + speed;
		return speed;
	}
	
	string summary() {
		return "[vehicle] " + name + "  speed=" + (speed with_precision 1);
	}
}

class car parent: vehicle {
	string name <- "car";
	float fuel <- 50.0;
	float consumption_per_km <- 0.08;
	
	float move() {
		if fuel <= 0.0 {
			speed <- 0.0;
			return 0.0;
		}
		float d <- super.move();
		fuel <- max(0.0, fuel - d * consumption_per_km);
		return d;
	}
	
	string summary() {
		return super.summary() + "  fuel=" + (fuel with_precision 1);
	}
}

global {
	init {
		car c <- car(name: "sedan", speed: 80.0, fuel: 40.0, max_speed: 180.0);
		bicycle b <- bicycle(name: "city-bike", speed: 15.0, rider: "Alice");
		
		// Polymorphic dispatch
		list<vehicle> fleet <- [c, b];
		
		loop times: 5 {
			loop v over: fleet {
				v.move();
			}
		}
		loop v over: fleet {
			write v.summary();   // calls overridden summary per type
		}
		
		// Type filtering
		list<vehicle> cars <- fleet select (each is car);
		write "Cars: " + cars collect each.name;
	}
}

experiment Inheritance title: "Inheritance" type: gui {}

Key Takeaways

Feature Syntax
Inherit from a class class Child parent: Parent {
Override an attribute Redefine it in the child with a new default value
Override a typed action Redefine with the same return type and parameters
Call parent action super.method() returns the parent's return value
Test object type obj is ClassName returns true for the class or any subclass
Polymorphic list list<Parent> items <- [child1, child2];
⚠️ **GitHub.com Fallback** ⚠️