
// define superclass
abstract class Shape {
	private String name;
	protected double area;

	public Shape() {
		System.out.println("Shape constructor...");
		name = "noname";
	}

	public Shape(String n) {
		System.out.println("Shape constructor with parameter...");
		name = n;
	}

	public double getArea() {
		return area;
	}

	@Override
	public String toString() {
		return "Name: " + name + "\n" + "Area: " + area + "\n";
	}
	
	public abstract void calculateArea();
}

// define subclass
final class Square extends Shape {
	private int width;
	private int height;

	public Square(int w, int h) {
		// implicitly call superclass constructor that takes no parameter
		System.out.println("Square constructor...");
		width = w;
		height = h;
	}

	// overloaded constructor
	public Square(int w, int h, String n) {
		// call superclass constructor that takes 1 parameter
		super(n);
		System.out.println("Square constructor with 3 parameters...");
		width = w;
		height = h;
	}

	public void calculateArea() {
		area = width * height;
	}

	@Override
	public String toString() {
		return "=== Square Details ===\n" + 
				"Width: " + width + "\n" + 
				"Height: " + height + "\n" + 
				super.toString(); // calling superclass method
	}
}

public class InheritanceExample {
	
	public static void main(String[] args) {
		
		Square s1 = new Square(10, 20);
		s1.calculateArea();
		System.out.println("The area for s1 is " + s1.getArea());

		Square s2 = new Square(12, 22, "s2");
		s2.calculateArea();
		System.out.println("The area for s2 is " + s2.getArea());
		System.out.println();

		System.out.println(s1);
		System.out.println(s2);
		
		// polymorphism
		Shape s3 = new Square(11, 22, "s3");
		s3.calculateArea();
		// s3 is always a Shape object
		System.out.println(s3);
		
		// during compile time, s3 will call toString() from Shape
		// during runtime, s3 will call toString() from Square
		
		// cannot create object from abstract class
//		Shape s4 = new Shape();	// error
		
	}
}
