Getters and Setters

Getters and setters are special methods that allow you to control how properties of a class are accessed and modified. Think of them as gatekeepers - they decide what happens when someone tries to read or write to your object's properties.

What are Getters?

A getter is a method that retrieves the value of a property. It's called automatically when you access a property. Getters are defined using the get keyword.

class Circle {
  double _radius = 0; // Private property
  
  // Getter for radius
  double get radius => _radius;
  
  // Getter for area (calculated property)
  double get area => 3.14159 * _radius * _radius;
}

void main() {
  Circle circle = Circle();
  circle._radius = 5; // Direct access (not recommended)
  
  print('Radius: ${circle.radius}'); // Using getter
  print('Area: ${circle.area}');     // Using calculated getter
}

What are Setters?

A setter is a method that sets the value of a property. It's called automatically when you assign a value to a property. Setters are defined using the set keyword.

class Temperature {
  double _celsius = 0;
  
  // Getter for celsius
  double get celsius => _celsius;
  
  // Setter for celsius with validation
  set celsius(double value) {
    if (value < -273.15) {
      throw ArgumentError('Temperature cannot be below absolute zero');
    }
    _celsius = value;
  }
  
  // Getter for fahrenheit (calculated)
  double get fahrenheit => (_celsius * 9/5) + 32;
  
  // Setter for fahrenheit
  set fahrenheit(double value) {
    _celsius = (value - 32) * 5/9;
  }
}

void main() {
  Temperature temp = Temperature();
  
  temp.celsius = 25;  // Using setter
  print('${temp.celsius}°C = ${temp.fahrenheit}°F');
  
  temp.fahrenheit = 100;  // Using fahrenheit setter
  print('${temp.celsius}°C = ${temp.fahrenheit}°F');
}

Why Use Getters and Setters?

BenefitDescriptionExample Use Case
ValidationCheck if the input is valid before settingAge cannot be negative
Calculated PropertiesCompute values on-the-flyArea from width and height
Data TransformationConvert data format when getting/settingConvert temperature units
EncapsulationHide internal implementation detailsProtect sensitive data
Logging/DebuggingTrack when properties are accessed/modifiedMonitor property changes

Practical Example: Bank Account

Here's a real-world example that demonstrates the power of getters and setters:

class BankAccount {
  double _balance = 0;
  String _accountNumber;
  
  BankAccount(this._accountNumber);
  
  // Getter for balance
  double get balance => _balance;
  
  // Getter for account number (read-only)
  String get accountNumber => _accountNumber;
  
  // Setter for balance with validation
  set balance(double amount) {
    if (amount < 0) {
      throw ArgumentError('Balance cannot be negative');
    }
    _balance = amount;
  }
  
  // Calculated getter for account status
  String get accountStatus {
    if (_balance > 10000) return 'Premium';
    if (_balance > 1000) return 'Standard';
    return 'Basic';
  }
  
  void deposit(double amount) {
    if (amount > 0) {
      _balance += amount;
    }
  }
  
  void withdraw(double amount) {
    if (amount > 0 && amount <= _balance) {
      _balance -= amount;
    }
  }
}

void main() {
  BankAccount account = BankAccount('ACC123456');
  
  account.deposit(5000);
  print('Balance: \$${account.balance}');
  print('Status: ${account.accountStatus}');
  print('Account: ${account.accountNumber}');
}

Key Points to Remember

  • Use private properties (starting with _) with getters and setters for better encapsulation
  • Getters can return calculated values without storing them
  • Setters can validate input before assigning values
  • You can have a getter without a setter (read-only property)
  • You can have a setter without a getter (write-only property, though rare)
  • Getters and setters make your code more maintainable and flexible

Getters and setters are like having a smart doorman for your class properties - they control who gets in, what they can see, and ensure everything stays organized and secure! 🏠🔐