Interfaces & Abstract Classes

Course 1 · Ch 6
Interfaces & Abstract Classes
A feature with no Java equivalent at all — resolving a name collision without picking a winner

This time Java arrived first: java1-6's default interface methods shipped in Java 8 (2014), years before C# 8 (2019) added the same idea to C#. But C# didn't stop there — it added something Java's interface system has no equivalent for at all.

Interfaces — Recap

interface IMovable { void Move(double distance); // implicitly public — no modifier written }

Same underlying idea as java1-6 — a pure contract, a class implements ... except C# spells that keyword :, the same syntax used for class inheritance. Interface members are implicitly public, with no modifier needed.

Default Interface Methods (C# 8) — Java Arrived First This Time

interface IMovable { void Move(double distance); string Stop() => "Stopping."; // a real default body, C# 8 (2019) }

C# 8 added default interface method bodies — the identical idea java1-6 described for Java 8, five years earlier. Worth stating plainly, since it varies chapter to chapter: sometimes C# arrives first (switch expressions, records), and sometimes Java does, as here.

Multiple Interface Implementation — The Diamond Problem, Again

interface Greeter { string Greet() => "Hello from Greeter"; } interface Welcomer { string Greet() => "Hello from Welcomer"; } class Host : Greeter, Welcomer { public string Greet() => ((Greeter)this).Greet(); // explicit resolution required, like java1-6 }

The same narrow diamond problem java1-6 covered resurfaces here — two conflicting default implementations force an explicit resolution, or the class fails to compile. The syntax differs (a cast to the interface type, rather than Java's InterfaceName.super.method()), but the underlying requirement is identical: the compiler refuses to guess.

Explicit Interface Implementation — No Java Equivalent

interface IPrintable { void Process(); } interface ISavable { void Process(); } class Document : IPrintable, ISavable { void IPrintable.Process() { Console.WriteLine("Printing..."); } // TWO separate implementations — void ISavable.Process() { Console.WriteLine("Saving..."); } // no collision at all } Document doc = new Document(); ((IPrintable)doc).Process(); // "Printing..." ((ISavable)doc).Process(); // "Saving..." — the SAME object, two distinct behaviors

Here's the genuine reveal: java1-6's own diamond-problem fix forces a single, explicit resolution — one implementation wins. C#'s explicit interface implementation lets a class keep both conflicting implementations simultaneously, each one only reachable through the specific interface type it belongs to. This resolves a real name-collision scenario Java's interface system has no mechanism for at all — Java would require renaming one of the two methods; C# doesn't.

Abstract Classes — Quick Recap

abstract class Shape { public abstract double Area(); // no body — subclasses must supply one public string Describe() => $"Area: {Area()}"; }

Same idea as java1-6's own abstract classes — real state, constructors, and concrete methods alongside abstract ones, still limited to single inheritance.

FeatureJava (java1-6)C#
Default method bodiesJava 8 (2014) — firstC# 8 (2019)
Conflicting defaults from two interfacesforced single explicit overrideforced single explicit resolution, same idea
Same method name, two distinct behaviorsnot possible — must rename oneexplicit interface implementation — both kept
Reach for explicit interface implementation for genuine name collisions
When two interfaces a class implements both need a method with the identical name but different meanings, explicit interface implementation keeps both behaviors distinguishable by which interface reference is used to call them — no renaming required on either side.
An explicitly-implemented member isn't reachable through the class type
doc.Process() does not compile at all if Process() was only implemented explicitly for IPrintable/ISavable — it's only reachable via a variable or cast of the specific interface type, never through the concrete class type directly. This is easy to be caught out by the first time it's encountered.

Coding Challenges

Challenge 1

Write an interface IGreetable with an abstract Hello() method and a default Bye() method, then a class implementing IGreetable that only supplies Hello(), demonstrating Bye() is inherited automatically.

📄 View solution
Challenge 2

Write two interfaces IPrintable and ISavable that both declare a Process() method, and a Document class using explicit interface implementation to give each a genuinely different body. Call both through interface-typed casts of the same instance.

📄 View solution
Challenge 3

Using Challenge 2's Document class, attempt to call doc.Process() directly on a Document-typed variable (not cast to either interface). Show the resulting compile error and explain why it happens.

📄 View solution

Chapter 6 Quick Reference

  • Default interface methods shipped in C# 8 (2019) — years after java1-6's own Java 8 (2014) version
  • Conflicting defaults from two interfaces still force explicit resolution, the same requirement java1-6 covered for Java
  • Explicit interface implementation lets one class keep two genuinely different same-named methods — no Java equivalent exists
  • An explicitly-implemented member is reachable ONLY through the specific interface type, never through the concrete class type
  • Abstract classes work the same as java1-6's own — state, constructors, and concrete methods alongside abstract ones, still single inheritance
  • Next chapter: exception handling — every exception is unchecked, a real departure from java1-7's checked/unchecked split