Hiprup

What is the difference between an abstract class and an interface in Java, and when would you use each?

An abstract class is a partially implemented class declared with the abstract keyword — it can hold instance fields, constructors, and both abstract and concrete methods, but it cannot be instantiated, and a class may extends only one. An interface is primarily a contract: its abstract methods are implicitly public, its fields are implicitly public static final, and a class can implements many of them. Since Java 8, interfaces may also carry default and static methods with bodies.

  • Inheritance model — a class extends a single abstract class but can implement multiple interfaces, giving Java multiple inheritance of type without the diamond problem for state.

  • State and constructors — abstract classes own instance variables and constructors for subclass initialization; interfaces allow only constants and have no constructors.

  • Access modifiers — abstract-class members may be private or protected; interface members are public by default, with private helper methods allowed since Java 9.

  • When to choose — use an abstract class for closely related classes that share code and state (an "is-a" base); use an interface to define a capability unrelated classes can fulfil (a "can-do" contract).

  • Automation practice — frameworks use an abstract BaseTest for shared WebDriver setup and teardown, while Selenium's WebDriver is an interface implemented by ChromeDriver and FirefoxDriver, and TestNG listeners implement ITestListener.

Key terms: abstract class, interface, extends, implements, default methods, multiple inheritance, contract, BaseTest, WebDriver

// Interface: a capability contract (like Selenium's WebDriver)
interface Browser {
    String name();                        // implicitly public abstract
    default void open(String url) {       // Java 8+ default method
        System.out.println(name() + " opening " + url);
    }
}

// Abstract class: shared state + partial implementation
abstract class BaseTest {
    protected String env;                 // instance state allowed
    BaseTest(String env) { this.env = env; }  // constructor allowed
    void setUp() { System.out.println("Setup on " + env); }
    abstract void runTest();              // forces subclasses to implement
}

class LoginTest extends BaseTest implements Browser {
    LoginTest() { super("QA"); }
    public String name() { return "Chrome"; }
    void runTest() { System.out.println("Login test running"); }
}

public class Demo {
    public static void main(String[] args) {
        LoginTest t = new LoginTest();
        t.setUp();                  // Output: Setup on QA
        t.open("https://app.io");   // Output: Chrome opening https://app.io
        t.runTest();                // Output: Login test running
    }
}

LoginTest extends one abstract class (BaseTest) while also implementing an interface (Browser), showing both mechanisms working together. BaseTest contributes shared state (the env field), a constructor, and a reusable setUp() method, while its abstract runTest() forces every subclass to provide its own test logic.

Browser contributes a contract plus a Java 8 default method (open), which LoginTest inherits without writing any code — note that name() must be declared public in LoginTest because it implements an interface method. This mirrors how a real framework combines an abstract BaseTest with Selenium's WebDriver interface.

Stand out by mentioning the Java 8 shift — default and static methods narrowed the gap, so the real deciding factor today is state and constructors versus multiple inheritance of type — and by giving the Selenium example that WebDriver is an interface while your BaseTest is an abstract class. Expect follow-ups like "Can an abstract class have zero abstract methods?" (yes), "Can an interface have a constructor?" (no), and "What happens if a class inherits the same default method from two interfaces?" (it must override it, optionally delegating via InterfaceName.super.method()).

The classic mistake is claiming interfaces cannot contain method bodies, which has been wrong since Java 8.