Abstract class

You went to your local restaurant for breakfast. When you got there, you saw a lump of raw dough sitting in front of the stove. Then a boy came over and asked, “Ruti, paratha, or naan?” Whatever you say, they prepare it right on the spot and serve it to you.

Here the dough on the counter is the abstract class.

abstract class Dough{}

It exits, but it has no final form. You can not eat it raw rough dough. In programming we would say, you can not initiate it.

When a boy asked you what would you said Ruti, then the boy tell the cooker to prepare Ruti. Here in programming we would say, extends the class.

class Ruti extends Dough {}

Now it can be served and you can eat.

When we use the abstract keyword, it refers to an idea, and we don’t know who or how it should execute.

After eating you ordered some food to take home.

  • 4 ruti
  • 3 paratha
  • some naans

So now the prepared others types; In programming we gonna have other classes and create objects.

class Paratha extends Dough{}
class Naan extends Dough{}

Generic

But to take home, we need to put theses into separate bags. So first each item wrap with bag; In programming we can have a generic class that can acts as a wrapper.

We know dart is a strong dataType language. So we can not simply put int into String variable;

abstract class Dough {}

class Ruti extends Dough {}
class Paratha extends Dough {}
class Naan extends Dough {}

class RutiBox<T> {
  const RutiBox(this.ruti);
  final T ruti;
}

void main() {
  Ruti ruti = Ruti();
  RutiBox box = RutiBox(ruti);
}

specific generic class

But our current generic class have one issue. We can wrap the box with a box :) loss of the business owner.

void main() {
  Ruti ruti = Ruti();
  RutiBox box = RutiBox(RutiBox(ruti));
}

As a good programmer we can avoid it, by specifying

class RutiBox<T extends Dough> {
  const RutiBox(this.ruti);
  final T ruti;
}

void main() {
  Ruti ruti = Ruti();
  RutiBox box = RutiBox(RutiBox(ruti)); // this will show error
  RutiBox box = RutiBox(ruti); // But this is fine
}

We can have function, extensions, classes as generic. You can have more than one generic.

void printDetails<T, U>(T name, U age) {
  print("I am $name and $age years old");
}
printDetails("Rahim", 25);     // T becomes String, U becomes int
printDetails("Karim", "thirty"); // T becomes String, U becomes String (fully flexible!)

You can checkout dart List, Map dataType or how statefulWidget’s state class.


abstract class Input {
  const Input();
  void show();
}

class IntInput extends Input {
  const IntInput(this.value);
  final int value;

  void show() => print(value.toString());
}

class StringInput extends Input {
  const StringInput(this.value);
  final String value;

  void show() => print(value);
}

void main() {
  Input v = StringInput("2");
  v = IntInput(3);
  v.show();
}

But what if we want to Have a class that adapt any class we pass. If we see our Map and List in dart, it support any dataType. We can add, get, modify and performs others specific task.

What If we want a single Animal class, can be used for any type of data returns.

class Animal {
  const Success(this.name);
  final String name;
}

Now not all data will be String, we can have any type of data on Success right? So what can we can do? Create N class just because of each Success contains Different types of result? Well, let’s see how Generic can be better solution here.

class Success<T> {
  const Success(this.data);
  final T data;

  // we have methods as well
  Success<T> copyWith(T data) {
    return Success(data);
  }
}

Now we can pass any datatype for Success.

void main() {
  var result = Success<String>("Hi");
  result = result.copyWith(1); // this well show compile time error
  print(result.data);

  // But we can have a Different object with Different dataType
  var result2 = Success<int>(1);
  result2 = result2.copyWith(1);

  var result3 = Success<Success<int>>(result2);
  print(result3.data.data);
}

Extends generic

We can force that not Every-Type will not be accepted but Only subclass of something is acceptable.

sealed class ApiState {
  const ApiState();
}

class Success<T extends ApiState> {
  const Success(this.data);
  final T data;

  Success<T> copyWith(T data) {
    return Success(data);
  }
}

Now we only accept any subclass of ApiState.