DEV Community

loading...

C# Intro to the Abstract classes

gamersuji profile image Sujith ・9 min read

What are Abstract classes?

Abstract classes are a combination of both a normal class (default class) and an Interface.

Abstract class as an Interface: Like an interface you're not allowed to have an instance of an abstract class anywhere and also you're allowed to create "declaration only" methods like the methods inside interfaces and can later demand the inheritor of the abstract class to write the definitions.

Abstract class as a normal class: Like normal classes, you're allowed to have member variables, methods with definition and all the other advantages which will be covered in this post.

Let's jump into an example to see how to create an abstract class.

Let's write a scenario where we put a human to sleep at night. We can easily pull this off by creating a function Sleep in a separate class, Writing a functionality in a separate class enables the class to be independent and most importantly it will be really easier to be used in more than a single recipient class.
So, we create the Sleep as an abstract class.( There is a reason behind using an abstract class instead of a normal class. We will explain that in a bit but before that let's just see how to create an abstract class first.)

Creating an abstract class

To make a class into an "abstract" class just put an abstract keyword right in front of the class keyword.

    public abstract class Sleep: MonoBehaviour
    {
      public bool sleep;
        public void GetSleep()
        {
          //the inherited organisms will sleep whence this method is called
          sleep = true;
        }

    }

The Sleep abstract class has a method named GetSleep() which switches the sleep boolean to true. Upon making the sleep to true it will put the instance of a class which inherits the Sleep class to sleep.(we will expand on the "the inherited organism will sleep" comment that has been written inside the sleep method after writing the rest of the structure required for a human to sleep).

Let's start writing the class that inherits the Sleep class.

As planned this inheriting class will be the class Human. Since we've already wrote the functionality for the sleep inside the Sleep class. Thus we will pretty much leave the whole human class empty and inherit the sleep functionality from the Sleep abstract class. One of the main reasons to inherit this behavior instead of just writing this as part of the Human class is that the Human class does not have to know what sleep is.

This is one of the main reasons an abstract class is called an abstract class.

The inherited class doesn't need to know the structure of the class that is inside the class that it inherits. It is just like an actual human, A human like you who wishes to sleep. You close your eyes and expect yourself to be put to sleep, but you as a conscious self have no control over the process of sleep, there is no "sleep" button in your head that puts you to sleep. For you, the entire concept of sleep is abstract. You know how sleep can be triggered but cannot directly put yourself to sleep.
The best thing you can do is to close your eyes and wait till your brain puts you to sleep. So like a human who doesn't need to know the internal structure of sleep, this Human class doesn't need to know the internal structure of a Sleep class and how it does the sleep process. For the Human class the internal structure of a Sleep class is abstract.

    public class Human : Sleep
    {

    }

Now the Human class is capable of sleeping whenever GetSleep() is called over any human subject, Let's do that in a class called Night. Because the night cycle is one of the pottential triggers for Humans or any living organisms to sleep.

public class Night : MonoBehaviour {
 private GameObject human;

    private void PutEverybodyToSleep()
    {
        Sleep sleep = human.GetComponent<Sleep>();

        if(sleep != null)
        {
            sleep.GetSleep();
        }
    }
}

Here we have a GameObject(An Unity centric object, It serves as just a reference that holds the class Human over it). Now we do a GetComponent to get an instance of the Sleep from the Human GameObject and if it actually has an instance of Sleep(we confirm it by doing a a null check) we call the GetSleep() method to put the human subject to sleep. Since the Human class directly inherits the Sleep the GetSleep method had become part of the Human class.
As you have seen the Human class has nothing written in it, All it did was inherit a Sleep class into it and all of sleep functionality came on to it for the Night class to call it.

I promised right through the read that I will answer the following two questions in no chronological order.

1) The comment of GetSleep() inside the sleep class saying, "the inherited organisms will sleep whence this method is called"
2) Why abstract intstead of a normal class ?

I will answer the first question first, The reason to say that the inherited organisms instead of an organism because we were only talking about a human at that point of time and this GetSleep() was not just built for humans. The GetSleep() method inside the Sleep class upon inheriting will be a generic method that puts any organism to sleep as "sleep" is a common behaviour for all living organisms. So we can just inherit this class on any class types that require sleep behavior.


Before answering my second question I would like to remind you again that an abstract class is very similar to a normal class. Both are capable of handling a constructor, both are capable of creating local/global variables inside (just like the sleep boolean inside the Sleep class), both are capable of encapsulating(hiding data from children classes or other unrelated classes) both member variables and methods inside a class. The last and the most important similarity in both types of classes are, they are capable of handling virtual methods.

Wait! what are virtual methods?

Virtual methods are like any other method where they will have a method definition and will execute that definition upon calling that method. As an added advantage, If these methods are being inherited from a parent class these classes can be modified in the children's class by adding new definitions along with the existing definition or one can even over-write the entire definition provided by the parent class with a new set of definitions by using the "override" keyword.

Let's write a short virtual method inside our sleep class

To make any method a virtual method add the "virtual" keyword after an access specifier and before the return type of a method. Here in our example, there is a Dream method that generates a basic not so nightmarish, non-eccentric dream upon called by any of the children's class instance.

public abstract class Sleep : MonoBehaviour
    {
        public bool sleep;

        public void GetSleep()
        {
            //the inherited organisms will sleep whence this method is called
            sleep = true;
        }

        public virtual void Dream()
        {
            BasicDream();
        }

        private void BasicDream()
        {
            //A basic not so nightmarish, non-eccentric dream  
        }

    }
}

It is not mandatory for the children class to override the virtual method.

 public class Human : Sleep
    {
        public void GetSleep()
        {

        }

    }

This should not throw you any compile time or run time errors, but if you'd like to change the definition, You're given a free hand to do so. Let's assume that our human subject is suffering from some sort of sleep disorder and has a hard time sleeping. We cannot expect our subject to be having a pleasant dream during the course of his slumber. So we override the existing Dream method to give him a nightmare.

public class Human : Sleep
    {
        public void GetSleep()
        {

        }

        public override void Dream()
        {
            //bring him some ghosts, clowns and zombies to wreck his sleep
        }

    }

Now if you call the Dream() for an instance of a Human class, This human instead of having a non-nightmarish dream, he will see ghosts, clowns, and zombies in his dreams.
Here we have overridden the previous definition and changed it to give our human subject a taste of hell. But don't you feel like we have been too hard on our test subject? I definitely feel like it. So let's give him a normal non-nightmarish dream after all of that nightmare. As you know this "non-nightmarish dream" functionality is already part of the base Dream() method definition situated inside the parent class Sleep. Thus we do not have to rewrite it. To call the base definition inside an overridden method just use the keyword "base.MethodName()" (in our case it is base.Dream()) to use the base definition written inside the parent class.

        public override void Dream()
        {
            //bring him some ghosts, clowns and zombies to wreck his sleep
            base.Dream();
        }

The base class can be called inside an overridden method in no particular order. It can be either called before the overridden definitions or in-between the overridden definitions or even after the overridden definitions as we did above.

Now that you've learned virtual methods, we can now continue with answering the second question which is "Why abstract class instead of a normal class"?

As you could see normal classes and abstract classes are very similar and there is only one reason that should probably make you use an abstract class instead of a normal class. It is the need for an Interface like methods. In an Interface, methods are just declared and not defined. The ability to define these methods will be made mandatory for the classes that inherit the interface. The same way we can create such methods in abstract classes by preceding the method with the "abstract" keyword. Just add "abstract" keyword after an access specifier and before the return type of a method to make a method an abstract method.

We now use the same old Sleep class to create an abstract method called EstablishSleepTime(). As you could see that the sleep time can change in between species to species, every species should be able to establish their own definition of managing their sleep time.
Remember these abstract methods will not have definitions inside and the compiler will throw an error even if you try to give one.

public abstract class Sleep : MonoBehaviour
    {
        public bool sleep;

        public void GetSleep()
        {
            //the inherited organisms will sleep whence this method is called
            sleep = true;
        }

        public virtual void Dream()
        {
            BasicDream();
        }

        private void BasicDream()
        {
            //A basic non nightmar-ish, non eccentric dream  
        }

        public abstract void EstablishSleepTime();


    }
}

And also remember that the abstract methods can only be written in an abstract class.

Whence this class is inherited, the inherited class (in our case the Human class) will have to establish the amount of he sleep any human should be getting.

Writing definitions for an abstract method can only be done by overriding the abstract method inside the inherited class just like overriding a virtual method inside an inherited class

    public class Human : Sleep
    {
        public void GetSleep()
        {

        }

        public override void Dream()
        {
            //bring him some ghosts, clowns and zombies to wreck his sleep
            base.Dream();
        }

        public override void EstablishSleepTime()
        {
            //get 8 hours of sleep for this human
        }
    }

Here inside the EstablishSleepTime() method we define the amount of time a human can sleep. Since this EstablishSleepTime() is made as an abstract method in the parent class this method is made as a mandatory class to be defined in the children's class.

This is the main reason why we use an abstract class instead of a normal class.

The other main use of an abstract class:
If you could see the abstract class Sleep you can witness that it is a very common behavior of every living thing. Even non-living things like computers sleep when they're tired. So this functionality can be inherited by any class that requires sleep. If you had written the sleep functionality inside every single class it would only be a duplicate of the same functionality inside every single class that requires sleep. Since abstract is capable of being a replacement of a normal class, We write this once in a class and we directly inherit it and use it anywhere like how we would do with a normal default class.

Conclusion

Abstract classes are a combination of both a normal class and an interface. A normal method cannot handle an abstract method and Interfaces are incapable of using constructors, encapsulation, member variables, and method definitions. But the abstract class is capable of doing what both of these classes cannot do. If your class structure demands for such a class to be inherited then you should use an abstract class.

Discussion (1)

pic
Editor guide
Collapse
lordsayur profile image
Omar

Can a normal class inherit more than 1 abstract classes?