OOPS! Deep Dive 3: Inheritance (super / base)
Object-Oriented Programmingbeginnerdotnetjavaoop
Site Admin · 11/12/2026
Build a Parking Lot, part 3 of 5. Code and practice for the video, in Java and C#.
What you'll learn
- Turn Vehicle into a parent class, with Car and Bike as child classes (extends in Java, : in C#)
- Call the parent's constructor first: super(...) in Java, : base(...) in C#
- Use the IS-A test, and keep fields private, or protected for child classes
- Remove the type check: each kind of vehicle passes its own rate to the parent
Download
- parking-lot-dd03-java.zip: Java (lesson code, practice starter, solution)
- parking-lot-dd03-csharp.zip: C# (the same)
The code from the video
Java
Vehicle.java
// Deep Dive 3: Vehicle is now the parent class. Car and Bike extend it.
public class Vehicle {
private final String plate;
protected final int hourlyRate; // protected: child classes can read it
public Vehicle(String plate, int hourlyRate) {
if (plate == null || plate.isBlank())
throw new IllegalArgumentException("plate is required");
this.plate = plate;
this.hourlyRate = hourlyRate;
}
public String getPlate() { return plate; }
public int getHourlyRate() { return hourlyRate; }
public void describe() {
System.out.println(getClass().getSimpleName() + " " + plate);
}
}ParkingSpot.java
public class ParkingSpot {
private final int number;
private Vehicle parked; // null while free
public ParkingSpot(int number) {
if (number < 1)
throw new IllegalArgumentException("spot number must be 1 or more");
this.number = number;
}
public int getNumber() { return number; }
public boolean isFree() { return parked == null; }
public Ticket park(Vehicle v, int hour) {
if (!isFree())
throw new IllegalStateException("spot " + number + " is taken");
parked = v;
return new Ticket(v, this, hour); // this = this spot
}
public Vehicle leave() {
Vehicle v = parked; // null if the spot was already free: no crash
parked = null;
return v;
}
}Ticket.java
public class Ticket {
private static int nextId = 1; // one counter, shared by all tickets
private final int id;
private final String plate;
private final int rate;
private final int spotNumber;
private final int entryHour;
public Ticket(Vehicle v, ParkingSpot s, int entryHour) {
if (entryHour < 0 || entryHour > 23)
throw new IllegalArgumentException("hour must be 0 to 23");
this.id = nextId++;
this.plate = v.getPlate();
this.rate = v.getHourlyRate(); // no type check: the vehicle knows its rate
this.spotNumber = s.getNumber();
this.entryHour = entryHour;
}
public static int issued() { return nextId - 1; }
public int fee(int exitHour) {
return Math.max(1, exitHour - entryHour) * rate;
}
public void print() {
System.out.println("Ticket #" + id + " | " + plate + " | spot "
+ spotNumber + " | in at " + entryHour + ":00");
}
}Bike.java
public class Bike extends Vehicle {
public Bike(String plate) {
super(plate, 10);
}
}Car.java
public class Car extends Vehicle {
private final boolean electric;
public Car(String plate, boolean electric) {
super(plate, 20); // build the parent part first
this.electric = electric;
}
public Car(String plate) {
this(plate, false);
}
public boolean isElectric() { return electric; }
}Main.java
public class Main {
public static void main(String[] args) {
Car car = new Car("ABC-1234");
Bike bike = new Bike("XYZ-77");
Car ev = new Car("EVX-2025", true);
ParkingSpot spot1 = new ParkingSpot(1);
ParkingSpot spot2 = new ParkingSpot(2);
ParkingSpot spot3 = new ParkingSpot(3);
Ticket t1 = spot1.park(car, 9); // a Car IS-A Vehicle
Ticket t2 = spot2.park(bike, 10);
Ticket t3 = spot3.park(ev, 11);
car.describe();
bike.describe();
ev.describe();
System.out.println("Car fee at 12:00 = " + t1.fee(12));
System.out.println("Bike fee at 12:00 = " + t2.fee(12));
System.out.println("EV fee at 12:00 = " + t3.fee(12));
}
}Output:
Car ABC-1234
Bike XYZ-77
Car EVX-2025
Car fee at 12:00 = 60
Bike fee at 12:00 = 20
EV fee at 12:00 = 20C#
Vehicle.cs
// Deep Dive 3: Vehicle is now the parent class. Car and Bike extend it.
public class Vehicle
{
private readonly string plate;
protected readonly int hourlyRate; // protected: child classes can read it
public Vehicle(string plate, int hourlyRate)
{
if (string.IsNullOrWhiteSpace(plate))
throw new ArgumentException("plate is required");
this.plate = plate;
this.hourlyRate = hourlyRate;
}
public string Plate => plate;
public int HourlyRate => hourlyRate;
public void Describe()
{
Console.WriteLine(GetType().Name + " " + plate);
}
}ParkingSpot.cs
public class ParkingSpot
{
private readonly int number;
private Vehicle parked; // null while free
public ParkingSpot(int number)
{
if (number < 1)
throw new ArgumentException("spot number must be 1 or more");
this.number = number;
}
public int Number => number;
public bool IsFree() => parked == null;
public Ticket Park(Vehicle v, int hour)
{
if (!IsFree())
throw new InvalidOperationException("spot " + number + " is taken");
parked = v;
return new Ticket(v, this, hour); // this = this spot
}
public Vehicle Leave()
{
Vehicle v = parked; // null if the spot was already free: no crash
parked = null;
return v;
}
}Ticket.cs
public class Ticket
{
private static int nextId = 1; // one counter, shared by all tickets
private readonly int id;
private readonly string plate;
private readonly int rate;
private readonly int spotNumber;
private readonly int entryHour;
public Ticket(Vehicle v, ParkingSpot s, int entryHour)
{
if (entryHour < 0 || entryHour > 23)
throw new ArgumentException("hour must be 0 to 23");
this.id = nextId++;
this.plate = v.Plate;
this.rate = v.HourlyRate; // no type check: the vehicle knows its rate
this.spotNumber = s.Number;
this.entryHour = entryHour;
}
public static int Issued() => nextId - 1;
public int Fee(int exitHour) => Math.Max(1, exitHour - entryHour) * rate;
public void Print()
{
Console.WriteLine("Ticket #" + id + " | " + plate + " | spot "
+ spotNumber + " | in at " + entryHour + ":00");
}
}Bike.cs
public class Bike : Vehicle
{
public Bike(string plate) : base(plate, 10)
{
}
}Car.cs
public class Car : Vehicle
{
private readonly bool electric;
public Car(string plate, bool electric) : base(plate, 20) // build the parent part first
{
this.electric = electric;
}
public Car(string plate) : this(plate, false)
{
}
public bool IsElectric => electric;
}Program.cs
public class Program
{
public static void Main(string[] args)
{
Car car = new Car("ABC-1234");
Bike bike = new Bike("XYZ-77");
Car ev = new Car("EVX-2025", true);
ParkingSpot spot1 = new ParkingSpot(1);
ParkingSpot spot2 = new ParkingSpot(2);
ParkingSpot spot3 = new ParkingSpot(3);
Ticket t1 = spot1.Park(car, 9); // a Car IS-A Vehicle
Ticket t2 = spot2.Park(bike, 10);
Ticket t3 = spot3.Park(ev, 11);
car.Describe();
bike.Describe();
ev.Describe();
Console.WriteLine("Car fee at 12:00 = " + t1.Fee(12));
Console.WriteLine("Bike fee at 12:00 = " + t2.Fee(12));
Console.WriteLine("EV fee at 12:00 = " + t3.Fee(12));
}
}Output:
Car ABC-1234
Bike XYZ-77
Car EVX-2025
Car fee at 12:00 = 60
Bike fee at 12:00 = 20
EV fee at 12:00 = 20Practice: Add a Truck
Practice (Deep Dive 3): trucks are coming to the lot.
1. Write a class Truck that extends Vehicle, at 40 per hour.
2. Give it its own field: int axles.
3. Constructor: Truck(String plate, int axles). Call the parent constructor first.
4. Add getAxles().
Bonus: reject fewer than 2 axles
(throw new IllegalArgumentException("a truck has at least 2 axles")).
Then uncomment the truck lines in Main.java. Expected: Truck fee at 12:00 = 120(Names above are the Java ones; the C# starter below uses the C# names in its comments.)
Start from the lesson code above. Only these files change; the rest are the same as the lesson.
Starter (Java)
Truck.java
// Practice (Deep Dive 3): trucks are coming to the lot.
//
// 1. Write a class Truck that extends Vehicle, at 40 per hour.
// 2. Give it its own field: int axles.
// 3. Constructor: Truck(String plate, int axles). Call the parent constructor first.
// 4. Add getAxles().
// Bonus: reject fewer than 2 axles
// (throw new IllegalArgumentException("a truck has at least 2 axles")).
//
// Then uncomment the truck lines in Main.java. Expected: Truck fee at 12:00 = 120Main.java
public class Main {
public static void main(String[] args) {
Car car = new Car("ABC-1234");
Bike bike = new Bike("XYZ-77");
Car ev = new Car("EVX-2025", true);
ParkingSpot spot1 = new ParkingSpot(1);
ParkingSpot spot2 = new ParkingSpot(2);
ParkingSpot spot3 = new ParkingSpot(3);
Ticket t1 = spot1.park(car, 9); // a Car IS-A Vehicle
Ticket t2 = spot2.park(bike, 10);
Ticket t3 = spot3.park(ev, 11);
car.describe();
bike.describe();
ev.describe();
System.out.println("Car fee at 12:00 = " + t1.fee(12));
System.out.println("Bike fee at 12:00 = " + t2.fee(12));
System.out.println("EV fee at 12:00 = " + t3.fee(12));
// When Truck is done, uncomment these lines:
// ParkingSpot spot4 = new ParkingSpot(4);
// Truck truck = new Truck("TRK-4040", 3);
// Ticket t4 = spot4.park(truck, 9);
// truck.describe();
// System.out.println("Truck fee at 12:00 = " + t4.fee(12));
}
}Starter (C#)
Truck.cs
// Practice (Deep Dive 3): trucks are coming to the lot.
//
// 1. Write a class Truck that extends Vehicle (Truck : Vehicle), at 40 per hour.
// 2. Give it its own field: int axles.
// 3. Constructor: Truck(string plate, int axles). Call the parent constructor first (: base(...)).
// 4. Add a read-only property Axles.
// Bonus: reject fewer than 2 axles
// (throw new ArgumentException("a truck has at least 2 axles")).
//
// Then uncomment the truck lines in Program.cs. Expected: Truck fee at 12:00 = 120Program.cs
public class Program
{
public static void Main(string[] args)
{
Car car = new Car("ABC-1234");
Bike bike = new Bike("XYZ-77");
Car ev = new Car("EVX-2025", true);
ParkingSpot spot1 = new ParkingSpot(1);
ParkingSpot spot2 = new ParkingSpot(2);
ParkingSpot spot3 = new ParkingSpot(3);
Ticket t1 = spot1.Park(car, 9); // a Car IS-A Vehicle
Ticket t2 = spot2.Park(bike, 10);
Ticket t3 = spot3.Park(ev, 11);
car.Describe();
bike.Describe();
ev.Describe();
Console.WriteLine("Car fee at 12:00 = " + t1.Fee(12));
Console.WriteLine("Bike fee at 12:00 = " + t2.Fee(12));
Console.WriteLine("EV fee at 12:00 = " + t3.Fee(12));
// When Truck is done, uncomment these lines:
// ParkingSpot spot4 = new ParkingSpot(4);
// Truck truck = new Truck("TRK-4040", 3);
// Ticket t4 = spot4.Park(truck, 9);
// truck.Describe();
// Console.WriteLine("Truck fee at 12:00 = " + t4.Fee(12));
}
}Solution
Try it yourself first!
Show the Java solution
Truck.java
public class Truck extends Vehicle {
private final int axles;
public Truck(String plate, int axles) {
super(plate, 40); // the parent part first
if (axles < 2)
throw new IllegalArgumentException("a truck has at least 2 axles");
this.axles = axles;
}
public int getAxles() { return axles; }
}Main.java
public class Main {
public static void main(String[] args) {
Car car = new Car("ABC-1234");
Bike bike = new Bike("XYZ-77");
Car ev = new Car("EVX-2025", true);
ParkingSpot spot1 = new ParkingSpot(1);
ParkingSpot spot2 = new ParkingSpot(2);
ParkingSpot spot3 = new ParkingSpot(3);
Ticket t1 = spot1.park(car, 9); // a Car IS-A Vehicle
Ticket t2 = spot2.park(bike, 10);
Ticket t3 = spot3.park(ev, 11);
car.describe();
bike.describe();
ev.describe();
System.out.println("Car fee at 12:00 = " + t1.fee(12));
System.out.println("Bike fee at 12:00 = " + t2.fee(12));
System.out.println("EV fee at 12:00 = " + t3.fee(12));
ParkingSpot spot4 = new ParkingSpot(4);
Truck truck = new Truck("TRK-4040", 3);
Ticket t4 = spot4.park(truck, 9);
truck.describe();
System.out.println("Truck fee at 12:00 = " + t4.fee(12));
}
}Output:
Car ABC-1234
Bike XYZ-77
Car EVX-2025
Car fee at 12:00 = 60
Bike fee at 12:00 = 20
EV fee at 12:00 = 20
Truck TRK-4040
Truck fee at 12:00 = 120Show the C# solution
Truck.cs
public class Truck : Vehicle
{
private readonly int axles;
public Truck(string plate, int axles) : base(plate, 40) // the parent part first
{
if (axles < 2)
throw new ArgumentException("a truck has at least 2 axles");
this.axles = axles;
}
public int Axles => axles;
}Program.cs
public class Program
{
public static void Main(string[] args)
{
Car car = new Car("ABC-1234");
Bike bike = new Bike("XYZ-77");
Car ev = new Car("EVX-2025", true);
ParkingSpot spot1 = new ParkingSpot(1);
ParkingSpot spot2 = new ParkingSpot(2);
ParkingSpot spot3 = new ParkingSpot(3);
Ticket t1 = spot1.Park(car, 9); // a Car IS-A Vehicle
Ticket t2 = spot2.Park(bike, 10);
Ticket t3 = spot3.Park(ev, 11);
car.Describe();
bike.Describe();
ev.Describe();
Console.WriteLine("Car fee at 12:00 = " + t1.Fee(12));
Console.WriteLine("Bike fee at 12:00 = " + t2.Fee(12));
Console.WriteLine("EV fee at 12:00 = " + t3.Fee(12));
ParkingSpot spot4 = new ParkingSpot(4);
Truck truck = new Truck("TRK-4040", 3);
Ticket t4 = spot4.Park(truck, 9);
truck.Describe();
Console.WriteLine("Truck fee at 12:00 = " + t4.Fee(12));
}
}Output:
Car ABC-1234
Bike XYZ-77
Car EVX-2025
Car fee at 12:00 = 60
Bike fee at 12:00 = 20
EV fee at 12:00 = 20
Truck TRK-4040
Truck fee at 12:00 = 120How to run
- Java (17 or newer): in the folder,
javac -d out *.javathenjava -cp out Main - C# (.NET SDK 9 or newer): in the folder,
dotnet run
Previous: Deep Dive 2: Encapsulation, this & static · Next: Deep Dive 4: Polymorphism, Abstraction & Interfaces
Comments
Loading comments…