Why Should We Upcast Browser Driver Class Object To WebDriver?

In interview a frequently asked interview question is “Why do we upcast browser class object to WebDriver? or why we do not upcast to RemoteWebDriver class instead of WebDriver?” and we give all sort of answers.

First of all this question is asked with assumption that we do not have any option except upcast. Is it so? Absolutely not. It completely up to you whether you upcast or not and if you do then to which level? Question should be “Why should we upcast it or what are advantages if you upcast?

Before we start thinking this particular topic only with respect to Selenium WebDriver-Java, let’s learn core Java concepts i.e. Inheritance and upcast.

I define an Interface in Java as “A contract. I want something to do but I do not know how to do the same. I need a class or multiple classes to achieve that.“. This may not should exactly with purpose of Interface but at high level this we do.

“Amod” ( My name ) wants to perform swapping of two numbers in Java but he does not know the logic to do. So he has created an interface as below:-

package InheritanceExamples;

public interface WhatAmodWantsButDontKnowHowToDo {
	
	void swapTwoIntVariables(int a, int b);

}

Amod has a best friend “John” who has average programming knowledge and promises to give implementation of Amod’s method. Amod is so happy now. John gives implementation as below:-

package InheritanceExamples;

public class JohnKnowsImplementation implements WhatAmodWantsButDontKnowHowToDo{

	@Override
	public void swapTwoIntVariables(int a, int b) {
		System.out.println("--------------Before swap------------");
		System.out.println("a :" + a);
		System.out.println("b :" + b);
		/// Swap logic using temporary
		int c = a;
		a = b;
		b = c;
		System.out.println("--------------After swap------------");
		System.out.println("a :" + a);
		System.out.println("b :" + b);
	
	}

}

Now Amod creates a usage utility to run his incomplete method as below:-

package InheritanceExamples;

public class AmodUsesImplementationGivenByJohn {
	
	public static void main(String[] args) {
		
		JohnKnowsImplementation johnKnowsImplementation = new JohnKnowsImplementation();
		johnKnowsImplementation.swapTwoIntVariables(10, 20);
	}

}

Output :-

--------------Before swap------------
 a :10
 b :20
--------------After swap------------
 a :20
 b :10

Amod is happy now and everywhere he is appreciating John. One day he met his best mentor Mukesh who is a great developer. He says to Amod that he can provide a more optimal and memory efficient implementation of swapping method. Amod is more happy now and asked for the implementation from Mukesh. Mukesh gives implementation as below:-

package InheritanceExamples;

public class MukeshKnowsOptimalImplementation implements WhatAmodWantsButDontKnowHowToDo{

	@Override
	public void swapTwoIntVariables(int a, int b) {
		System.out.println("--------------Before swap------------");
		System.out.println("a :" + a);
		System.out.println("b :" + b);
		/// Swap logic without using temporary
		a = a+b;
		b = a -b;
		a = a-b;
		System.out.println("--------------After swap------------");
		System.out.println("a :" + a);
		System.out.println("b :" + b);
	
	}

}

Now Amod creates a new usage utility class so that he can use Mukesh’s implementation.

package InheritanceExamples;

public class AmodUsesImplementationGivenByMukesh {
	
	public static void main(String[] args) {
		
		MukeshKnowsOptimalImplementation mukeshKnowsOptimalImplementation = new MukeshKnowsOptimalImplementation();
		mukeshKnowsOptimalImplementation.swapTwoIntVariables(10, 20);
	}

}

As Amod is using Mukesh’s implementation it makes John sad. He says to Amod, he is not doing good with him. Amod does not want to make sad any one. He is in tension now what to do. He is also thinking if there is another new implementation given by someone else again he needs to create a new usage class and same problem may arise again.

He found a solution in core concept of Interface, Inheritance and runtime method overriding as below:-

package InheritanceExamples;

public class UpcastImplementation {
	
	public static void main(String[] args) {
		
		WhatAmodWantsButDontKnowHowToDo whatAmodWantsButDontKnowHowToDo;
		// Using John's Implementation
		whatAmodWantsButDontKnowHowToDo = new JohnKnowsImplementation();
		whatAmodWantsButDontKnowHowToDo.swapTwoIntVariables(10, 20);
		// Using Mukesh's Implementation
		whatAmodWantsButDontKnowHowToDo = new MukeshKnowsOptimalImplementation();
		whatAmodWantsButDontKnowHowToDo.swapTwoIntVariables(10, 20);
	}

}

Amod has created a reference of his Interface as whatAmodWantsButDontKnowHowToDo. Since both John and Mukesh’s implementation class implement Amod’s interface, whatAmodWantsButDontKnowHowToDo can hold any object of implemented class. If there is further new implementations of Amod’s method, he does not need to create a new class anymore. When John’s comes to meet Amod then he sees his implementation is being used and same for Mukesh. ( Please assume the game :-p Hypothetical statement ).

Story over but did you get the point of story?

Implementation Classes of WebDriver Interface

Above classes in image are directly or indirectly implementations of WebDriver interface. In fact RemoteWebDriver is direct implemented class of WebDriver and remaining browser classes extends RemoteWebDriver. There is another update I can see in Selenium 4 that, a new class ChromiumDriver is introduced which extends RemoteWebDriver and ChromeDriver class extends ChromiumDriver now.

We know WebDriver is an interface and these implemented class complete it. Many implemented classes represent a browser. You can related WebDriver interface as WhatAmodWantsButDontKnowHowToDo interface and ChromeDriver , FirefoxDriver etc as MukeshKnowsOptimalImplementation and JohnKnowsImplementation.

Let’s talk with respect to Selenium WebDriver now

When we do not upcast to WebDriver

Suppose, you need to create a base class which need to be extended in every class and perform some actions. User should be able to launch any browser.

You will create a base class as below:

package InheritanceExamples;


import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.safari.SafariDriver;

public class WithOutUpcastToWebDriver {

	// Since user should be able to launch any browser , we need to create reference object of every browser type
	public static ChromeDriver cDriver;
	public static FirefoxDriver fDriver;
	public static InternetExplorerDriver iDriver;
	public static EdgeDriver eDriver;
	public static SafariDriver sDriver;
	
	// We need to keep adding browser driver reference variable
}