// Create instances
Shape circle = new Circle(5.0);
Shape rectangle = new Rectangle(4.0, 6.0);
Shape triangle = new Triangle(3.0, 4.0);

// Pattern matching with instanceof (Java 16+)
public static double calculateArea(Shape shape) {
    if (shape instanceof Circle c) {
        return c.area();
    } else if (shape instanceof Rectangle r) {
        return r.area();
    } else if (shape instanceof Triangle t) {
        return t.area();
    }
    // Compiler knows all cases are covered
    return 0;
}

// Switch expressions with pattern matching (Java 17+)
public static String describe(Shape shape) {
    return switch (shape) {
        case Circle c -> "Circle with radius " + c.radius;
        case Rectangle r -> "Rectangle with dimensions " + r.width + "x" + r.height;
        case Triangle t -> "Triangle with base " + t.base + " and height " + t.height;
        // Compiler ensures exhaustiveness
    };
}

// Expression evaluation
Expression expr1 = new Constant(5.0);
Expression expr2 = new Variable("x", 3.0);
Expression expr3 = new Addition(expr1, expr2);
Expression expr4 = new Multiplication(expr3, new Constant(2.0));

double result = expr4.evaluate(); // (5 + 3) * 2 = 16

// Pattern matching on expressions
public static String format(Expression expr) {
    return switch (expr) {
        case Constant c -> String.valueOf(c.value());
        case Variable v -> v.name();
        case Addition a -> "(" + format(a.left()) + " + " + format(a.right()) + ")";
        case Multiplication m -> "(" + format(m.left()) + " * " + format(m.right()) + ")";
    };
}