Classes and Methods

The previous lesson grouped a player's data into one structure. But data rarely travels alone: there are rules about it. A score gets awarded points, never set to arbitrary garbage; an account gets debited, only if the balance allows. A class is the construct that keeps the data and its rules in one place, and it is the organising idea behind most of the world's production code.

A class is a blueprint; objects are the houses built from it. The blueprint says every house has rooms and a front door, but nobody lives in a blueprint. Each built house has its own actual rooms and its own key. In code: the Player class says every player has a name and a score; each player object built from it carries its own name and its own score, and changing one player touches no other.

Blueprint versus instance

Two moments, like defining versus calling a function. Writing the class declares what every instance will contain and what it can do; nothing exists yet. Constructing an instance, usually through a special function called a constructor, allocates a fresh object with its own copies of the fields. A hundred constructions give a hundred independent objects, all following one definition. When the class changes, every instance built afterward follows the new rules: one definition, everywhere behaviour, exactly the function-reuse argument at a larger scale.

A method is a function that knows its object

The only genuinely new mechanic in this lesson is small: a method is a function attached to a class, and when you call p.award(25), the object p is handed to the method as an invisible extra argument. Most languages name it self or this. Inside award, self.score means "the score of whichever object this call was made on". Same method, different object, different data touched. That is the entire trick.

Once you see the hidden argument, methods stop being magic: p.award(25) is award(p, 25) wearing nicer clothes. Lua makes this visible in the syntax itself, with the colon in p:award(25) being sugar for p.award(p, 25). Python makes you write self by hand in every method definition. The other languages pass this silently. Under every one of them is the same plain function with an extra parameter.

One class, seven spellings

The switcher below builds the same Player class in each language: a constructor storing a name and a zero score, an award method, one instance, one call. Ruby and Python are the tidiest. Lua assembles classes by hand out of tables and metatables, which looks alien but is the same blueprint-instance-method triangle. Go refuses the class keyword entirely and attaches methods directly to a struct.

Deeper down, method calls are a lookup: the runtime takes the object, finds its class (its metatable in Lua, its prototype in JavaScript, its vtable in compiled languages), and locates the named method there. That lookup-by-name is why dynamic languages can add or replace methods at runtime, and it has the same consequence keys had in the last lesson: a method that anything might look up by name is dangerous to rename. Inheritance, classes extending other classes, deepens the lookup chain but not the principle, and this course needs nothing more than the principle.

Classes in shipped code

Class structure survives shipping better than almost anything else. Java and C# compile to bytecode that preserves class names, method names, field names, and signatures so faithfully that a decompiler recovers something close to the original source: openInventory, verifyPurchase, KeyValidator. That is a labelled map of the program, for free.

This is why name-level obfuscation matters most in exactly those languages, and it is also where the safety limit bites hardest: a method that external code, a framework, or reflection calls by name must keep that name, or the calls break. Every serious Java obfuscator is, at heart, an engine for deciding which names are safe to destroy. The honest summary: class-heavy languages leak the most structure by default and constrain renaming the most, and both facts follow from the same design choice of looking things up by name.

A class is a blueprint bundling fields with the methods allowed to touch them; each constructed instance carries its own data. A method is a plain function that receives its object as a hidden argument. And because method lookup runs on names, those names both survive into shipped code and resist careless renaming.

Frequently asked questions

Do I have to use classes to write good programs?

No. Plenty of excellent code is plain functions over plain data, Go and Lua lean that way culturally, and small scripts rarely benefit from ceremony. Classes earn their keep when many pieces of data share both shape and rules. This course needs you to read class-based code, not to prefer it.

What about inheritance?

Inheritance lets one class extend another, adding or overriding methods, and the method lookup walks up the chain until it finds a match. It is powerful and famously easy to overuse. For this course, the lookup-by-name mechanism is the part that matters, because it is what shapes both decompiler output and renaming safety.

Keep learning

  • Objects, Maps, and Tables
  • Scope: Where Names Live
  • Identifier Renaming: The First Layer, Never the Last
  • What a Decompiler Does
  • Java & JAR Obfuscator
  • Lua Obfuscator
  • Python Obfuscator

All lessons