Menu
Coddy logo textTech

Generic Classes

Part of the Object Oriented Programming section of Coddy's Dart journey — lesson 65 of 110.

You've seen how Dart's built-in collections like List<String> use type parameters. Now it's time to create your own generic classes that work with any type you specify.

A generic class uses a type parameter (typically T) as a placeholder for the actual type that will be provided when creating an instance:

class Box<T> {
  T content;
  
  Box(this.content);
  
  T getContent() => content;
}

void main() {
  var stringBox = Box<String>('Hello');
  var intBox = Box<int>(42);
  
  print(stringBox.getContent());  // Hello
  print(intBox.getContent());     // 42
}

The T acts as a type variable - when you create Box<String>, every T in the class becomes String. This gives you type safety without writing separate classes for each type.

You can use multiple type parameters when your class needs to work with more than one type:

class Pair<K, V> {
  K first;
  V second;
  
  Pair(this.first, this.second);
}

void main() {
  var pair = Pair<String, int>('age', 25);
  print('${pair.first}: ${pair.second}');  // age: 25
}

Generic classes are the foundation of reusable, type-safe code. Instead of duplicating logic for different types or losing type information with dynamic, generics let you write flexible code that the compiler can still verify.

challenge icon

Challenge

Easy

Let's build a gift wrapping system that uses generic classes to create flexible containers for different types of items. You'll create a generic Wrapper class that can wrap any type of content, and a GiftBox class with two type parameters to pair a gift with a card message.

You'll organize your code into two files:

  • wrapper.dart: Define your generic classes here:
    • A generic class Wrapper<T> with a T content property and a constructor. Include a method unwrap() that returns the content, and a method describe() that prints Wrapped: [content]
    • A generic class GiftBox<G, C> with two properties: G gift and C card. Include a constructor and a method open() that prints Gift: [gift] on one line and Card: [card] on the next line
  • main.dart: Import your wrapper file and demonstrate the generic classes with different types:
    • Create a Wrapper<String> containing 'Chocolate' and call describe()
    • Create a Wrapper<int> containing 42 and call describe()
    • Create a Wrapper<double> containing 3.14 and call describe()
    • Print an empty line
    • Create a GiftBox<String, String> with gift 'Teddy Bear' and card 'Happy Birthday!', then call open()
    • Print an empty line
    • Create a GiftBox<int, String> with gift 100 (representing a gift card amount) and card 'Congratulations!', then call open()

Notice how the same Wrapper class works seamlessly with strings, integers, and doubles. The GiftBox class demonstrates using two type parameters to pair different types together - the gift can be any type while the card message remains a string.

Expected output:

Wrapped: Chocolate
Wrapped: 42
Wrapped: 3.14

Gift: Teddy Bear
Card: Happy Birthday!

Gift: 100
Card: Congratulations!

Cheat sheet

Generic classes use type parameters (like T) as placeholders for actual types specified when creating instances:

class Box<T> {
  T content;
  
  Box(this.content);
  
  T getContent() => content;
}

void main() {
  var stringBox = Box<String>('Hello');
  var intBox = Box<int>(42);
  
  print(stringBox.getContent());  // Hello
  print(intBox.getContent());     // 42
}

When you create Box<String>, every T in the class becomes String, providing type safety without separate classes for each type.

Use multiple type parameters when working with more than one type:

class Pair<K, V> {
  K first;
  V second;
  
  Pair(this.first, this.second);
}

void main() {
  var pair = Pair<String, int>('age', 25);
  print('${pair.first}: ${pair.second}');  // age: 25
}

Try it yourself

import 'wrapper.dart';

void main() {
  // TODO: Create a Wrapper<String> containing 'Chocolate' and call describe()
  
  // TODO: Create a Wrapper<int> containing 42 and call describe()
  
  // TODO: Create a Wrapper<double> containing 3.14 and call describe()
  
  // TODO: Print an empty line
  
  // TODO: Create a GiftBox<String, String> with gift 'Teddy Bear' 
  // and card 'Happy Birthday!', then call open()
  
  // TODO: Print an empty line
  
  // TODO: Create a GiftBox<int, String> with gift 100 
  // and card 'Congratulations!', then call open()
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming