# Encapsulation

Course: [Dart](https://www.devtutorials.ai/learn/dart) · Section: Object-Oriented Programming
Source: https://www.devtutorials.ai/learn/dart/object-oriented/encapsulation

> How to hide an object's data with private names, and control how it is read and changed with getters, setters and computed properties.

## What Is Encapsulation?

**Encapsulation** = keeping an object's data **inside** the object, and letting outside code use it only through **doors you choose**.

- The data is hidden. Outside code cannot change it directly.
- The doors are **getters** (to read) and **setters** (to change).
- A door can **check** a value before letting it in.

**Simple lesson:** encapsulation means "hide the data, and guard the doors".

### A Real-World Example

Think of the school canteen card.

- The card holds your money, but you cannot write a new amount on it with a pen.
- To add money, the counter checks your payment first.
- To spend, the machine checks that you have enough.
- So the amount on the card is always correct.

Every **Run** box on this page runs on the course's Dart runner in your browser.

Privacy in Dart works **per file (library)**. Each Run box is one file, so the examples show the idea and tell you what happens in a second file.

## What You Will Learn

In this lesson, you will learn:

1. Why we hide data, and what "controlled access" means.
2. How the `_` prefix makes a name private.
3. How to write getters and read-only properties.
4. How to check values in setters.
5. How to make computed properties that are worked out on demand.

## Data Hiding

### Why Hide Data?

Here is a class with **no** protection:

```dart runnable title="Anyone can break the marks"
class ReportCard {
  String name;
  int marks;
  ReportCard(this.name, this.marks);
}

void main() {
  var card = ReportCard('Asha', 92);
  card.marks = -40;
  print('${card.name}: ${card.marks}');
}
```

- Marks can never be below 0, but the program accepted `-40`.
- Any line, anywhere, can set any value.
- In a big app, finding which line did it is very hard.

### Controlled Access

**Controlled access** = outside code reads and changes data **only through methods or properties** that the class gives.

- The class decides **what** can be read.
- The class decides **what** can be changed, and **how**.
- Bad values are stopped at the door, in one place.

**Simple lesson:** if only the class can change its data, the class can keep that data correct.

## Public and Private Members

### The `_` Prefix

- **Public** member = a normal name, like `marks`. Any file can use it.
- **Private** member = a name that starts with `_`, like `_marks`. Only code in the **same file** can use it.
- Dart has no `private` keyword. The `_` is the whole rule.
- You learned this rule for top-level names in **Libraries**. It works the same for fields and methods.

```dart runnable title="A private field with a public method"
class PiggyBank {
  int _coins = 0;

  void add(int n) {
    if (n > 0) {
      _coins += n;
    }
  }

  int count() => _coins;
}

void main() {
  var bank = PiggyBank();
  bank.add(10);
  bank.add(-5);
  bank.add(3);
  print(bank.count());
}
```

`add(-5)` was ignored, because the method checks the value.

### What Happens in Another File

If `PiggyBank` lives in `piggy_bank.dart` and your `main()` is in `main.dart`:

```dart
// main.dart
import 'piggy_bank.dart';

void main() {
  var bank = PiggyBank();
  bank._coins = 1000;   // Error
}
```

Real Dart stops this before running: `The setter '_coins' isn't defined for the class 'PiggyBank'`. From outside the file, `_coins` simply does not exist.

Inside the **same file**, `_` names are still visible. That is why the Run boxes here can touch `_coins` from `main()`.

In real projects, put each important class in its own file, so `_` really hides the data.

**Simple lesson:** start a name with `_` to keep it inside its file.

## Getters

### Writing a Getter

**Getter** = a method that you **read like a field**, with no `( )`.

```text
Type get name => value;
```

```dart runnable title="A getter for a private field"
class Student {
  final String name;
  int _marks;

  Student(this.name, this._marks);

  int get marks => _marks;
}

void main() {
  var s = Student('Ravi', 75);
  print(s.marks);
  print('${s.name} scored ${s.marks}');
}
```

- `s.marks` looks like a field, but it runs the getter.
- There is **no setter**, so `s.marks = 80;` is an error.

### Read-Only Properties

**Read-only property** = a value you can read but not change from outside.

Two ways to make one:

| Way | Code | Who can change it? |
|---|---|---|
| `final` field | `final String name;` | Nobody, after the constructor |
| private field + getter | `int _marks;` and `int get marks => _marks;` | Only the class itself |

```dart runnable expect-error title="Trying to change a final field"
class Student {
  final String rollNo;
  Student(this.rollNo);
}

void main() {
  var s = Student('7A-12');
  s.rollNo = '7A-99';
  print(s.rollNo);
}
```

**Simple lesson:** a getter lets outside code read a value without being able to change it.

## Setters

### Writing a Setter with Validation

**Setter** = a method that runs when you **assign** to a property: `obj.name = value`.

```text
set name(Type value) { ... }
```

Try it here first. The field is private, and the setter checks every value:

```dart runnable title="A setter that guards the marks"
class Student {
  final String name;
  int _marks = 0;

  Student(this.name);

  int get marks => _marks;

  set marks(int value) {
    if (value < 0 || value > 100) {
      print('Rejected $value: marks must be 0 to 100');
      return;
    }
    _marks = value;
  }
}

void main() {
  var s = Student('Priya');
  s.marks = 88;
  s.marks = 140;
  s.marks = -3;
  print('${s.name}: ${s.marks}');
}
```

### Throwing Instead of Ignoring

Printing a message is gentle. Often it is better to **stop** with an error, so the mistake is not hidden:

```dart runnable expect-error title="A setter that throws"
class Wallet {
  int _rupees = 0;

  int get rupees => _rupees;

  set rupees(int value) {
    if (value < 0) {
      throw ArgumentError('Money cannot be negative: $value');
    }
    _rupees = value;
  }
}

void main() {
  var w = Wallet();
  w.rupees = 250;
  print(w.rupees);
  w.rupees = -10;
  print('This line never runs');
}
```

You will learn to catch errors like this in **Exception Handling**.

**Simple lesson:** a setter is a guard at the door. It checks a value before saving it.

## Computed Properties

**Computed property** = a getter that **works out** its value each time, from other fields. Nothing extra is stored.

```dart runnable title="Total, average and grade, worked out on demand"
class ReportCard {
  final String name;
  final List<int> _marks;

  ReportCard(this.name, this._marks);

  int get total => _marks.fold(0, (a, b) => a + b);

  String get average => (total / _marks.length).toStringAsFixed(1);

  String get grade => total >= 240 ? 'A' : 'B';

  bool get passedAll => _marks.every((m) => m >= 35);
}

void main() {
  var card = ReportCard('Asha', [92, 85, 78]);
  print('Total: ${card.total}');
  print('Average: ${card.average}');
  print('Grade: ${card.grade}');
  print('Passed all: ${card.passedAll}');
}
```

- `total` is never saved. It is added up each time you read it.
- So it can **never be out of date**.
- Keep getters fast and simple. A getter should not change anything.

**Easy way to remember**

- `_name` = **private**: stays inside its file.
- `get` = **read door**: `obj.x`
- `set` = **write door with a guard**: `obj.x = v`
- A getter with no matching setter = **read-only**.

**Simple lesson:** a computed property gives a fresh answer every time, from the data already stored.

## TL;DR

1. **Encapsulation** = hide the data, and allow access only through doors you choose.
2. A name starting with **`_`** is private to its file (library).
3. **Getter** `Type get x => ...;` is read like a field.
   - A getter without a setter makes a **read-only** property.
4. **Setter** `set x(Type v) { ... }` runs on `obj.x = v`, and can check the value.
5. **Computed property** = a getter that works out its value from other fields.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Writing `private int marks;` | Dart has a `private` keyword | Start the name with `_` | A compile error. |
| Expecting `_` to hide data in the same file | `_` hides from all code | `_` hides from **other files** only | Code in the same file can still change it. |
| `int get marks() => _marks;` | Getters have `( )` | A getter has no parameter list | A compile error. |
| `set marks(int v) => marks = v;` | The setter can set itself | Set the private field: `_marks = v` | The setter calls itself forever: Stack Overflow. |
| Storing `total` as a field | It must be saved | Use a getter that adds it up | The saved total goes out of date. |
| A setter that does nothing on bad input | Silent is safe | Throw an error, or tell the user | Bugs hide quietly. |

## Check Yourself

## Exercises

## What to Remember

- Encapsulation hides the data and lets the class guard every change.
- A `_` name is private to its file. There is no `private` keyword.
- A getter is read like a field. Without a setter, the value is read-only.
- A setter can check a value before saving it, or throw an error.
- A computed property works out its value each time, so it is never out of date.

Next: **Inheritance**. You will learn to build a new class on top of an existing one.
