# Classes

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

> How to design classes with fields and methods, make objects, use this, static members and cascades, and build objects with default, named, redirecting, const and factory constructors.

## What Are Classes and Objects?

**Class** = a blueprint that describes a kind of thing: what it **has** and what it **can do**.

**Object** = one real thing made from that blueprint, with its own values.

For example:

- the class `Student` says every student has a name and an age, and can introduce themselves
- the object **Asha, 13** is one student
- the object **Ravi, 14** is another student, made from the same class

**Simple lesson:** a class is the plan, and objects are the things built from the plan.

### A Real-World Example

A school uniform shop has one **design** for the school shirt.

- The design says: a collar, a pocket, a school badge.
- The shop makes **many shirts** from that one design.
- Each shirt has its **own** size and its **own** name tag.

The design is the class. Each shirt is an object.

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

It supports classes with fields, methods, constructors and static members. Inheritance comes later, in **Inheritance**.

## What You Will Learn

In this lesson, you will learn:

1. What classes and objects are, and how to write them.
2. Fields, methods, `this`, static members and the cascade operator.
3. Default and parameterized constructors.
4. Named constructors, initializer lists and redirecting constructors.
5. Const and factory constructors.

## Object Fundamentals

### Everything Is an Object

In Dart, **every value is an object**, even numbers and text.

**Root type** = `Object`, the type at the top. Every class is a kind of `Object`.

```dart runnable title="Numbers and text are objects too"
void main() {
  print(5.isEven);
  print('Dart'.length);
  print(5 is Object);
  print('Dart' is Object);
  print([1, 2].runtimeType);
}
```

`isEven` and `length` are **members** of those objects. A dot (`.`) reaches a member.

### Object Members

Every object in Dart has these, because they come from `Object`:

| Member | What it does |
|---|---|
| `toString()` | gives the object as text. `print` calls it. |
| `hashCode` | a number used by sets and maps to store the object |
| `==` | checks whether two objects are equal |
| `runtimeType` | the object's type, right now |
| `noSuchMethod()` | runs when a missing member is called on a `dynamic` value |

You can change how `toString()` and `==` behave in your own classes (see below).

**Simple lesson:** every Dart value is an object, and every object has `toString()`, `hashCode` and `==`.

## Class Fundamentals

### Class Structure

```text
class ClassName {
  // fields: what each object HAS
  // constructor: how to MAKE an object
  // methods: what each object can DO
}
```

### Class Declaration

```dart runnable title="Your first class"
class Student {
  String name;
  int age;

  Student(this.name, this.age);

  void introduce() {
    print('Hi, I am $name and I am $age years old.');
  }
}

void main() {
  var asha = Student('Asha', 13);
  var ravi = Student('Ravi', 14);
  asha.introduce();
  ravi.introduce();
}
```

- **`String name;` and `int age;`** = fields. Every Student has them.
- **`Student(this.name, this.age);`** = the constructor. It puts the values into the fields (see constructors, below).
- **`void introduce()`** = a method. Every Student can do it.

### Class Naming Conventions

- Class names use **UpperCamelCase**: `Student`, `ReportCard`, `LibraryBook`.
- Use a **noun** (a thing), in the singular: `Student`, not `Students`.

Make objects from the class, then click one to call its method:

**Simple lesson:** a class groups fields and methods under one UpperCamelCase name.

## Objects

### Creating Objects

**Instance** = another word for an object made from a class.

- Call the class like a function: `Student('Asha', 13)`.
- In old Dart code you may see `new Student(...)`. The `new` is optional now.

### Accessing Object Members

Use a **dot**: `asha.name`, `asha.age = 14`, `asha.introduce()`.

```dart runnable title="Reading and changing fields"
class Student {
  String name;
  int age;

  Student(this.name, this.age);
}

void main() {
  var asha = Student('Asha', 13);
  print(asha.name);
  print(asha.age);
  asha.age = 14;
  print('After her birthday: ${asha.age}');
}
```

### Object References

**Reference** = a variable does not hold the object itself. It holds **where** the object is.

Two variables can point to the **same** object:

**`identical(a, b)`** = `true` only if both point to the **very same** object.

Passing an object to a function also passes the reference.

If the function changes the object's fields, the caller sees the change too. Make a new object when you need a separate copy.

**Simple lesson:** a variable holds a reference, so two variables can share one object.

## Instance Variables

**Instance variable (field)** = a variable that belongs to each object. Every object has its own copy.

### Type Annotations and Default Values

```dart runnable title="Fields with types and starting values"
class ReportCard {
  String studentName;
  int maths = 0;
  int science = 0;
  String? remark;

  ReportCard(this.studentName);
}

void main() {
  var card = ReportCard('Priya');
  print(card.maths);
  print(card.remark);
  card.maths = 91;
  card.remark = 'Excellent';
  print('${card.studentName}: ${card.maths}, ${card.remark}');
}
```

- `int maths = 0;` = every new card starts with 0.
- `String? remark;` = may be empty, so it starts as `null` (see **Null Safety**).
- A non-nullable field with no value **must** be set by the constructor, or Dart refuses the class.

**Simple lesson:** each object has its own fields, and each field needs a starting value.

## Methods

**Instance method** = a function that belongs to a class, and works with that object's fields.

### Method Parameters and Return Values

```dart runnable title="Methods that take inputs and give answers"
class ReportCard {
  String name;
  int maths;
  int science;

  ReportCard(this.name, this.maths, this.science);

  int total() {
    return maths + science;
  }

  bool hasPassed(int passMark) => maths >= passMark && science >= passMark;

  void addBonus(int marks) {
    maths += marks;
  }
}

void main() {
  var card = ReportCard('Kiran', 72, 30);
  print(card.total());
  print(card.hasPassed(35));
  card.addBonus(5);
  print(card.maths);
}
```

- Methods use the object's fields **directly**: `maths + science`.
- They can take parameters and return values, like any function (see **Functions**).

### Overriding toString()

By default, printing an object shows `Instance of 'ClassName'`. Give it a better `toString()`:

```dart runnable title="A better toString"
class Student {
  String name;
  int age;
  Student(this.name, this.age);
}

class Teacher {
  String name;
  String subject;
  Teacher(this.name, this.subject);

  @override
  String toString() => 'Teacher($name, $subject)';
}

void main() {
  print(Student('Asha', 13));
  print(Teacher('Mrs. Rao', 'Maths'));
}
```

**`@override`** = a note that says "this replaces a member I already had" (here, from `Object`).

**Simple lesson:** methods are what an object can do, and `toString()` decides how it prints.

## The this Keyword

**`this`** = the current object, the one the method was called on.

### Resolving Naming Conflicts

When a parameter has the same name as a field, the parameter wins. Use `this.` to reach the field:

```dart runnable title="this. picks the field"
class Student {
  String name;
  int age;

  Student(this.name, this.age);

  void rename(String name) {
    this.name = name;
  }

  void describe() {
    print('${this.name} is $age');
  }
}

void main() {
  var s = Student('Ashu', 13);
  s.rename('Asha');
  s.describe();
}
```

- In `rename`, `name` alone means the **parameter**. `this.name` means the **field**.
- In `describe`, there is no clash, so `age` and `this.age` mean the same. Dart style leaves `this.` out when it is not needed.

**Simple lesson:** use `this.` only when a parameter hides a field with the same name.

## Static Members

**Static member** = a field or method that belongs to the **class itself**, not to each object.

### Static Variables and Static Methods

```dart runnable title="Counting students with a static field"
class Student {
  static int count = 0;
  static const schoolName = 'Green Valley';

  String name;

  Student(this.name) {
    count++;
  }

  static String welcome() => 'Welcome to $schoolName!';
}

void main() {
  print(Student.welcome());
  print(Student.count);
  Student('Asha');
  Student('Ravi');
  Student('Priya');
  print(Student.count);
}
```

### Accessing Static Members

- Outside the class, write `Student.count` and `Student.welcome()`.
- Inside the class, you can write just `count`.
- A static method **cannot** use instance fields like `name`, because it does not belong to any one student.

**Simple lesson:** static members belong to the class and are shared, instance members belong to each object.

## The Cascade Operator

**Cascade (`..`)** = do several things to the **same** object, one after another, in one expression.

```dart runnable title="Setting up an object with cascades"
class ReportCard {
  String name = '';
  int maths = 0;
  int science = 0;

  void show() {
    print('$name: Maths $maths, Science $science');
  }
}

void main() {
  var card = ReportCard()
    ..name = 'Meena'
    ..maths = 88
    ..science = 93
    ..show();
  print(card.maths + card.science);
}
```

- Each `..` works on the object made by `ReportCard()`.
- The whole cascade gives back **that object**, so `card` gets it.
- **Method chaining** = calling one method after another in a single expression. Cascades let you chain even methods that return nothing.

### The Null-Aware Cascade (?..)

**`?..`** = start the cascade only if the object is not `null`.

```dart runnable title="?.. skips a null object"
class ReportCard {
  String name = '';
  void show() => print('Card for $name');
}

void main() {
  ReportCard? missing;
  missing?..name = 'Nobody'..show();
  var found = ReportCard()..name = 'Zoya';
  found..show();
}
```

**Easy way to remember**

- **class** = the cookie cutter. **object** = each cookie.
- **field** = what it has. **method** = what it does.
- **static** = one shared copy for the whole class.
- **..** = "and also do this, to the same object".

**Simple lesson:** `..` sets up one object step by step, and `?..` does nothing if the object is null.

## What Is a Constructor?

**Constructor** = a special function inside a class that **builds** a new object and sets up its fields.

- It has the **same name** as the class.
- It runs **once**, when the object is made.
- A class can have **several** constructors, for different ways of building.

For example, a `Student` can be built:

- from a name and an age
- as a "guest" with default values
- from a line of text like `'Ravi,14'`

**Simple lesson:** a constructor is the recipe Dart follows to build each new object.

### A Real-World Example

A school issues ID cards in different ways.

- A **new student** fills in a form with name and class.
- A **visitor** gets a ready-made "Guest" card.
- A **transfer student** brings an old card, and the office copies the details.

Three ways in, but every result is an ID card. Pick a constructor to see what it builds:

Some rules on this page are checked by real Dart before the program runs. Those are shown as plain code.

## Default Constructors

**Default constructor** = the constructor Dart gives you when you write none. It takes no inputs.

```dart runnable title="No constructor written, but objects can still be made"
class Bell {
  int rings = 0;

  void ring() {
    rings++;
    print('Ring! ($rings)');
  }
}

void main() {
  var bell = Bell();
  bell.ring();
  bell.ring();
}
```

Every field needs a starting value, because nothing sets it up.

**Simple lesson:** if you write no constructor, Dart adds an empty one for you.

## Parameterized Constructors

**Parameterized constructor** = a constructor that takes inputs and puts them into fields.

### The Long Way

```dart
class Student {
  late String name;
  late int age;

  Student(String name, int age) {
    this.name = name;
    this.age = age;
  }
}
```

### The Short Way: this.field

**Initializing formal** = a parameter written `this.name`. It puts the argument straight into the field.

```dart runnable title="this.field does the work for you"
class Student {
  String name;
  int age;

  Student(this.name, this.age);
}

void main() {
  var s = Student('Asha', 13);
  print('${s.name}, ${s.age}');
}
```

`Student(this.name, this.age);` does the same as the long way, in one line. This is the Dart style.

### Named and Optional Parameters

Constructors can use every parameter style from **Functions**:

```dart runnable title="Named, required and default values in a constructor"
class Student {
  String name;
  int age;
  String house;

  Student({required this.name, required this.age, this.house = 'Not chosen'});
}

void main() {
  var a = Student(name: 'Asha', age: 13, house: 'Tagore');
  var b = Student(age: 12, name: 'Kiran');
  print('${a.name}: ${a.house}');
  print('${b.name}: ${b.house}');
}
```

**Simple lesson:** `this.field` parameters fill fields directly, in any parameter style.

## Named Constructors

**Named constructor** = an extra constructor with its own name: `ClassName.name(...)`.

- Use it for a **different way** of building the same kind of object.
- The name explains the way: `Student.guest()`, `Student.fromText(...)`.

```dart runnable title="A second way to build a Student"
class Student {
  String name;
  int age;

  Student(this.name, this.age);

  Student.guest()
      : name = 'Guest',
        age = 0;

  @override
  String toString() => '$name ($age)';
}

void main() {
  print(Student('Asha', 13));
  print(Student.guest());
}
```

`Student.guest()` sets the fields itself, with an **initializer list** (the part after `:`).

Dart has no "overloading": two constructors cannot share a name, even with different inputs.

Named constructors solve this. Each way of building gets its own clear name.

**Simple lesson:** a named constructor is a second, clearly named recipe for the same class.

## Constructor Initializer Lists

**Initializer list** = the part after `:` that sets fields **before** the constructor body runs.

- Items are separated by commas.
- It can use the parameters, but not other methods of the object.
- It is the right place to set `final` fields from a calculation.

A `final` field must get its value before the body runs. In real Dart, setting it inside `{ }` is an error:

```dart
class Rectangle {
  final int area;
  Rectangle(int w, int h) {
    area = w * h;
  }
}
```

Dart says `Final field 'area' is not initialized`. Use an initializer list instead.

**Simple lesson:** an initializer list sets fields early, which is how `final` fields get computed values.

## Redirecting Constructors

**Redirecting constructor** = a constructor that just hands the work to another constructor with `: this(...)`.

```dart runnable title="A newcomer is a Student aged 11"
class Student {
  String name;
  int age;

  Student(this.name, this.age);

  Student.newcomer(String name) : this(name, 11);

  @override
  String toString() => '$name ($age)';
}

void main() {
  print(Student.newcomer('Kiran'));
  print(Student('Zoya', 12));
}
```

- No body is allowed. The other constructor does everything.
- It avoids writing the same set-up code twice.

**Simple lesson:** a redirecting constructor passes its inputs on to another constructor.

## Constant Constructors

**Constant constructor** = a `const` constructor. It lets you make **compile-time constant** objects (see **Variables and Constants**).

Rules:

- Every field must be `final`.
- The call uses `const`: `const Point(2, 3)`.

```dart runnable title="A const constructor"
class Point {
  final int x;
  final int y;

  const Point(this.x, this.y);

  @override
  String toString() => '($x, $y)';
}

void main() {
  const start = Point(0, 0);
  var finish = Point(4, 3);
  print(start);
  print(finish);
}
```

In real Dart, two `const Point(0, 0)` values are the **very same object**, so they share memory. This makes Flutter apps faster, because unchanged parts of the screen are built once.

**Simple lesson:** a `const` constructor makes objects that are fixed forever, and Dart can share them.

## Factory Constructors

**Factory constructor** = a constructor marked `factory`. It runs code, then **returns** an object.

- It can return a **new** object, or an **existing** one.
- It is good for building from other data, like text or a map.

```dart runnable title="Building from text with a factory"
class Student {
  String name;
  int age;

  Student(this.name, this.age);

  factory Student.fromText(String text) {
    var parts = text.split(',');
    return Student(parts[0].trim(), int.parse(parts[1].trim()));
  }

  @override
  String toString() => '$name ($age)';
}

void main() {
  var lines = ['Asha, 13', 'Ravi, 14'];
  for (var line in lines) {
    print(Student.fromText(line));
  }
}
```

### Returning an Existing Object

A factory can keep a **cache** and hand back the same object again:

```dart runnable title="One ID card per student, never two"
class IdCard {
  static final _issued = <String, IdCard>{};
  final String holder;

  IdCard._(this.holder);

  factory IdCard(String holder) {
    return _issued.putIfAbsent(holder, () => IdCard._(holder));
  }
}

void main() {
  var first = IdCard('Asha');
  var again = IdCard('Asha');
  var other = IdCard('Ravi');
  print(identical(first, again));
  print(identical(first, other));
}
```

- `IdCard._(...)` is a **private** named constructor (see **Libraries**). Only this file can call it.
- The factory gives back the card that was already issued.

You will use factories a lot in **File Handling**, as `fromJson`.

**Easy way to remember**

- `this.x` = fill the field from the argument.
- `.name(...)` = another way to build.
- `: x = ...` = set fields before the body.
- `: this(...)` = let another constructor do it.
- `const` = fixed forever. `factory` = run code, then return an object.

**Simple lesson:** a factory decides which object to return, so it can reuse objects or build them from data.

## TL;DR

1. A **class** is a blueprint. An **object** is one thing made from it: `Student('Asha')`.
2. **Fields** hold data. **Methods** do work. `this` means the current object.
3. **static** members belong to the class. `..` (cascade) does many things to one object.
4. **Constructors:** `Student(this.name);`, named `Student.guest()`, initializer lists `: x = 1`, redirecting `: this(...)`.
5. **const** constructors make fixed objects. **factory** constructors can return an existing or a different object.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| `class student` | Any case is fine | Class names use UpperCamelCase | It works, but every Dart reader is confused. |
| A non-nullable field with no value | Fields start empty | Set it in the constructor, or give a default | `Field must be initialized`. |
| `b = a`, then expecting a copy | `=` copies the object | `=` copies the reference | Changing `b` also changes `a`. |
| `name = name;` in a method | It sets the field | The parameter hides the field | The field never changes. Use `this.name`. |
| `asha.count` for a static field | Statics belong to objects | Use `Student.count` | An error. |
| A static method using `name` | Static methods see fields | They belong to no single object | An error. |
| Printing an object without `toString` | It prints its fields | It prints `Instance of 'Student'` | Unhelpful output. |
| A non-nullable field with no value | It starts empty | Set it in the constructor or give a default | `Non-nullable field must be initialized`. |
| Setting a `final` field in the body | The body runs first | Use `this.x` or an initializer list | `Final field is not initialized`. |
| `name = name;` in a constructor body | It sets the field | The parameter hides the field | Use `this.name = name;`, or `this.name` in the parameters. |
| A body on a redirecting constructor | It can add more code | It must only redirect | A compile error. |
| A `const` constructor with a non-final field | `const` makes fields final | Every field must be `final` | A compile error. |
| Using `this` in a factory | A factory has an object | No object exists yet | A compile error. |
| Two constructors with the same name | Dart picks by inputs | Every constructor needs its own name | `The constructor is already defined`. |

## Check Yourself

## Exercises

## What to Remember

- A class is a blueprint, and objects are made from it.
- Fields store data, methods do work, and `this` is the current object.
- Static members belong to the class, not to each object.
- `this.x` in a constructor saves a value straight into a field.
- Use named, const and factory constructors when one plain constructor is not enough.

Next: **Enums**. You will learn to describe a fixed set of choices, like the days of the week or the school houses.
