A method in programming is a function that belongs to a class, object or type and is invoked through it, so that it can read and change that object’s state. In object-oriented programming, methods express the behavior of an object, while fields or attributes hold its data, and together they form the public and private surface that the rest of a program uses.
The difference between a method and a free-standing function is not the code inside it but the binding to a receiver: the object on which the method is called is passed in implicitly, usually as this or self. That binding is what makes encapsulation, dynamic dispatch and polymorphism possible.
How a method call is resolved and executed
A method call such as account.withdraw(50) is resolved in two steps: the runtime or compiler first finds the method implementation that matches the receiver and the arguments, then executes it with the receiver bound to an implicit parameter. In Java, C# and C++ the receiver is named this; in Python it is the explicit first parameter self; in Go it is declared in front of the method name as a receiver parameter.
For virtual or overridable methods, the lookup happens at run time through a dispatch table (a vtable in C++ and most JVM and .NET implementations) or through a dictionary-based lookup along the class hierarchy in dynamic languages such as Python and Ruby. This dynamic dispatch lets code written against a base class or interface run the subclass version without knowing the concrete type. Non-virtual, static and final methods can be bound at compile time, which removes one indirection and allows inlining.
The table below summarizes how several mainstream languages expose the receiver and decide dispatch by default.
| Language | Receiver name | Default dispatch |
|---|---|---|
| Java | Implicit this | Virtual unless static, private or final |
| C# | Implicit this | Non-virtual unless marked virtual |
| C++ | Implicit this pointer | Non-virtual unless marked virtual |
| Python | Explicit self | Dynamic lookup by attribute name |
| Go | Named receiver parameter | Static on types, dynamic through interfaces |
| JavaScript | Implicit this, set by call site | Prototype chain lookup |
Kinds of methods and what each one does
Methods are classified by what they are bound to and what role they play in the class. An instance method works on one object and can access its fields. A static or class method belongs to the type itself and has no receiver object, which makes it suitable for factory functions and utilities. Abstract methods declare a signature without a body and force subclasses or implementers to supply one.
| Method kind | Bound to | Typical purpose |
|---|---|---|
| Instance method | A single object | Read or change that object’s state |
| Static method | The class or type | Factories, helpers, stateless logic |
| Abstract method | No body, declared only | Define a contract for subclasses |
| Constructor | A new object being created | Establish the initial valid state |
| Accessor (getter) | A single object | Expose a value without direct field access |
| Mutator (setter) | A single object | Change a value while enforcing rules |
| Extension method | An existing type, added externally | Add behavior without editing the type |
Many languages also distinguish query methods, which return information without side effects, from command methods, which change state and usually return nothing. Keeping these roles separate, an idea known as command-query separation, makes behavior easier to test and reason about.
Method signatures, overloading and overriding
A method signature is the part of the declaration that identifies the method for the compiler or runtime: its name and parameter list, and in some languages the receiver type. The return type, access modifier and declared exceptions complete the declaration but in Java and C# are not part of the signature used for overload resolution.
Two related mechanisms depend on signatures. Method overloading defines several methods with the same name and different parameter lists in one class, and the compiler chooses one at compile time from the argument types. Method overriding replaces an inherited method with a new implementation that has the same signature, and the choice happens at run time from the receiver’s actual class.
| Aspect | Method overloading | Method overriding |
|---|---|---|
| Where it occurs | Within one class or scope | Between a base class and a subclass |
| Signature | Same name, different parameters | Same name and parameters |
| Resolution time | Compile time | Run time |
| Purpose | Convenience for different inputs | Specialized behavior in subtypes |
| Language support | Java, C#, C++; not Python or Go | All class-based object-oriented languages |
Methods compared with functions, messages and properties
A method is a function with a receiver, but languages draw the boundary in different ways. In Smalltalk and Objective-C, calling a method is described as sending a message: the object decides at run time how to respond, and can even handle messages it has no method for. In Python, a function defined in a class becomes a bound method when accessed through an instance, which is an object pairing the function with its receiver.
Properties in C#, Kotlin and Python look like fields to the caller but are implemented as getter and setter methods. They allow validation or lazy computation to be added later without changing the calling code. A closure or lambda, by contrast, captures variables from its surrounding scope rather than from a receiver object, although both techniques can model behavior attached to state.
Designing good methods in practice
Well-designed methods are small, cohesive and named after what they do from the caller’s point of view, such as calculateTax() or isExpired(). A method should usually work at one level of abstraction and either answer a question or perform an action. Long parameter lists, often more than three or four arguments, signal that a parameter object or a separate class is missing.
The access modifier is a deliberate design decision. Public methods form the contract that other code depends on, so changing them is costly; private methods are implementation details that can be refactored freely. Preferring methods that tell an object to do something over methods that expose its data, often summarized as tell, do not ask, keeps the rules that protect an object’s invariants inside the object.
- Name methods with verbs for actions and with predicates such as
isorhasfor boolean queries. - Validate arguments at public boundaries and keep internal methods free of repeated checks.
- Return results rather than writing to shared state whenever the design allows it.
- Mark methods final, sealed or non-virtual when subclasses are not meant to change them.
Common pitfalls with methods
Most problems with methods come from unclear responsibility or unexpected binding. The table lists frequent symptoms, their causes and the usual remedies.
| Symptom | Likely cause | Typical fix |
|---|---|---|
| Lost `this` in JavaScript callbacks | Method passed without its receiver | Use bind, an arrow function or a wrapper |
| Subclass breaks base behavior | Override violates the base contract | Respect preconditions and postconditions |
| God method of hundreds of lines | Several responsibilities in one place | Extract smaller private methods |
| Wrong overload chosen | Implicit conversions or null arguments | Use distinct names or explicit casts |
| Constructor calls overridable method | Subclass state not yet initialized | Call only final or private methods there |
A further subtle issue is the fragile base class problem: when a base class method calls another overridable method internally, a subclass override can change the base behavior in ways the original author never anticipated. Composition over inheritance reduces this risk because collaborating objects interact only through their public methods.
In plain words
A method is an action that an object knows how to perform. If a program models a bank account as an object, the account has data such as its balance, and it has methods such as deposit and withdraw. The program does not change the balance directly; it asks the account to do it.
Think of a coffee machine. The buttons on the front are its methods: brew, add milk, clean. You press a button without opening the machine, and the machine follows its own rules inside. Methods let an object protect its own data while still being useful to everything around it.
A real-world example
A team building an online store models each shopping cart as a Cart object. Instead of letting other parts of the code edit the list of items directly, the class offers methods such as addItem(product, quantity), removeItem(productId) and total(). The addItem method rejects quantities below one and merges duplicate products, so every cart stays valid no matter which page calls it.
Later, the business introduces subscription carts that apply a 10 percent discount. A developer creates a SubscriptionCart subclass and overrides only the `total()` method. The checkout page keeps calling cart.total() without any change, and dynamic dispatch selects the right version for each cart. The new pricing ships with no edits to existing callers, and tests for the original cart continue to pass.