
Classes and data types are the two ways to define your own structured types in Vary. Classes bundle mutable state with methods. Data types are immutable value objects where the compiler generates equality, hashing, and copying for you. Neither supports inheritance. Use interfaces for polymorphism instead.

## Classes

```vary
class Counter(start: Int) {
    mut count: Int = start

    def increment(self) -> None {
        self.count = self.count + 1
    }

    def get(self) -> Int {
        return self.count
    }
}

let c = Counter(10)
c.increment()
print(c.get())          # 11
```

Class inheritance is not supported. Use interfaces for polymorphism.

## Data types

Auto-generate `equals`, `hashCode`, `toString`, and `copy`:

```vary
data Point {
    x: Int
    y: Int
}

let p1 = Point(1, 2)
print(p1)               # Point(x=1, y=2)
print(p1 == Point(1, 2)) # True

let p2 = p1.copy(x=10)  # Point(x=10, y=2)
```

Data types are immutable. Fields cannot be reassigned after construction.

## Custom equality and hashing

Regular classes (not `data` types) can opt into structural equality by defining an `equals` method and a matching `hash`:

```vary
class Colour(r: Int, g: Int, b: Int) {
    def equals(self, other: Colour) -> Bool {
        return self.r == other.r and self.g == other.g and self.b == other.b
    }

    def hash(self) -> Int {
        return self.r * 65536 + self.g * 256 + self.b
    }
}

let a = Colour(255, 0, 0)
let b = Colour(255, 0, 0)
print(a == b)   # True — dispatches to equals
```

The compiler generates a JVM `equals(Object)` bridge and a `hashCode()` from these methods, so instances work as keys in `Dict` and `Set` and compare correctly with `==` and `!=`. If you override `equals`, you should override `hash` as well. Two values that compare equal must hash equal. Data types continue to auto-generate both; there's no need to write them by hand on a `data` declaration.

## Invariants

Classes and data types can declare an `invariant {}` block. The runtime checks each expression after construction. A failed invariant throws `ContractViolation`.

```vary
data PositivePoint {
    invariant {
        self.x > 0
        self.y > 0
    }
    x: Int
    y: Int
}

let p = PositivePoint(3, 5)    # passes
let bad = PositivePoint(-1, 5)  # throws ContractViolation
```

See the [contracts](/docs/contracts/) page for the full reference, including invariants on regular classes.

## Interfaces

Define contracts with `interface`. A class can implement multiple interfaces:

```vary
interface Shape {
    def area(self) -> Float {
    }
    def perimeter(self) -> Float {
    }
}

class Circle(radius: Float) implements Shape {
    mut radius: Float = radius

    def area(self) -> Float {
        return 3.14159 * self.radius * self.radius
    }

    def perimeter(self) -> Float {
        return 2.0 * 3.14159 * self.radius
    }
}
```

```vary-snippet
interface Printable {
    def to_string(self) -> Str {
    }
}

class LabeledCircle implements Shape, Printable {
    # must implement area(), perimeter(), and to_string()
}
```

### Default methods

Interface methods can have implementations. Classes that implement the interface inherit the default and can override it:

```vary
interface Greeter {
    def greet(self) -> Str {
        return "Hello"
    }
    def formal_greet(self) -> Str {
    }
}

class EnglishGreeter implements Greeter {
    # inherits greet() default, must implement formal_greet()
    def formal_greet(self) -> Str {
        return "Good day"
    }
}

class FrenchGreeter implements Greeter {
    # overrides the default
    def greet(self) -> Str {
        return "Bonjour"
    }
    def formal_greet(self) -> Str {
        return "Enchante"
    }
}
```

A class implementing multiple interfaces with conflicting default methods must override the conflicting method to resolve the ambiguity.
