强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Vala 语言入门教程 / 06 - 泛型

第 6 章:泛型

泛型(Generics)允许你编写能处理多种类型的通用代码,同时保持类型安全。


6.1 泛型概述

6.1.1 为什么需要泛型

在没有泛型的情况下,如果你想创建一个能存储任意类型的容器,你有两个选择:

// 选择 1:使用 GLib.Value(类型不安全)
var list = new GLib.List<GLib.Value?> ();
Value v1 = 42;
Value v2 = "hello";
list.append (v1);
list.append (v2);
// 读取时需要强制转换,容易出错

// 选择 2:为每种类型写一个容器(代码重复)
class IntList { /* ... */ }
class StringList { /* ... */ }
class DoubleList { /* ... */ }

泛型解决了这个问题:

// 使用泛型(类型安全 + 代码复用)
var int_list = new GLib.List<int> ();
int_list.append (42);
int val = int_list.first ().data;  // 无需强制转换

var str_list = new GLib.List<string> ();
str_list.append ("hello");

6.1.2 Vala 泛型 vs C++ 模板 vs Java 泛型

特性Vala 泛型C++ 模板Java 泛型
实例化运行时(GBoxedCopy)编译时(代码生成)编译时(类型擦除)
类型检查编译时编译时编译时
性能有装箱开销零开销有装箱开销
代码膨胀可能
反射支持是(GObject 类型系统)有限是(反射)

6.2 泛型类

6.2.1 定义泛型类

// 泛型容器类
public class Container<T> {
    private T _item;

    public T item {
        get { return _item; }
        set { _item = value; }
    }

    public Container (T item) {
        _item = item;
    }

    public void print_item () {
        // 注意:不能直接用 %s 格式化泛型类型
        print ("容器中的元素已设置\n");
    }
}

void main () {
    // 使用 int 类型参数
    var int_container = new Container<int> (42);
    print ("整数容器: %d\n", int_container.item);
    int_container.item = 100;
    print ("整数容器: %d\n", int_container.item);

    // 使用 string 类型参数
    var str_container = new Container<string> ("Hello Vala");
    print ("字符串容器: %s\n", str_container.item);

    // 类型推断(Vala 可以自动推断泛型参数)
    var auto_container = new Container<double> (3.14);
    print ("浮点容器: %f\n", auto_container.item);
}

6.2.2 多个类型参数

// 键值对
public class Pair<K, V> {
    public K key { get; set; }
    public V value { get; set; }

    public Pair (K key, V value) {
        Object (key: key, value: value);
    }
}

// 三元组
public class Triple<A, B, C> {
    public A first { get; set; }
    public B second { get; set; }
    public C third { get; set; }

    public Triple (A first, B second, C third) {
        Object (first: first, second: second, third: third);
    }
}

void main () {
    var pair = new Pair<string, int> ("年龄", 30);
    print ("%s: %d\n", pair.key, pair.value);

    var triple = new Triple<string, int, bool> ("张三", 30, true);
    print ("%s, %d, %s\n", triple.first, triple.second,
           triple.third.to_string ());
}

6.3 泛型方法

6.3.1 泛型函数定义

// 泛型方法:交换两个值
void swap<T> (ref T a, ref T b) {
    T temp = a;
    a = b;
    b = temp;
}

// 泛型方法:打印任意类型
void print_value<T> (T value) {
    if (value is int) {
        print ("int: %d\n", (int) value);
    } else if (value is string) {
        print ("string: %s\n", (string) value);
    } else if (value is double) {
        print ("double: %f\n", (double) value);
    } else {
        print ("未知类型\n");
    }
}

void main () {
    // 交换整数
    int a = 10, b = 20;
    print ("交换前: a=%d, b=%d\n", a, b);
    swap<int> (ref a, ref b);
    print ("交换后: a=%d, b=%d\n", a, b);

    // 交换字符串
    string s1 = "Hello", s2 = "World";
    swap<string> (ref s1, ref s2);
    print ("交换后: s1=%s, s2=%s\n", s1, s2);

    // 打印不同类型
    print_value<int> (42);
    print_value<string> ("Vala");
    print_value<double> (3.14);
}

6.3.2 泛型方法与返回值

// 泛型方法:创建默认值
T default_value<T> () {
    if (typeof (T) == typeof (int)) {
        return (T) 0;
    } else if (typeof (T) == typeof (string)) {
        return (T) "";
    } else if (typeof (T) == typeof (bool)) {
        return (T) false;
    }
    // 对于引用类型,返回 null
    return (T) null;
}

// 泛型方法:安全获取第一个元素
T? first_or_default<T> (T[] array) {
    if (array.length > 0) {
        return array[0];
    }
    return default_value<T> ();
}

void main () {
    int[] numbers = {1, 2, 3, 4, 5};
    int? first_num = first_or_default<int> (numbers);
    print ("第一个数字: %d\n", first_num);

    string[] names = {"Alice", "Bob", "Charlie"};
    string? first_name = first_or_default<string> (names);
    print ("第一个名字: %s\n", first_name);

    int[] empty = {};
    int? default_num = first_or_default<int> (empty);
    print ("默认数字: %d\n", default_num);
}

6.4 泛型约束

6.4.1 类型约束

// 约束 T 必须是 Object 的子类
public class ObjectCache<T extends Object> {
    private GLib.HashTable<string, T> cache;

    construct {
        cache = new GLib.HashTable<string, T> (str_hash, str_equal);
    }

    public void put (string key, T value) {
        cache[key] = value;
    }

    public T? get (string key) {
        return cache[key];
    }

    public bool contains (string key) {
        return cache.contains (key);
    }
}

// 约束 T 必须实现某个接口
public class Repository<T extends Object> {
    private GLib.List<T> items = new GLib.List<T> ();

    public void add (T item) {
        items.append (item);
    }

    public GLib.List<T> get_all () {
        return items;
    }
}

6.4.2 接口约束

// 定义接口
public interface Comparable<T> {
    public abstract int compare_to (T other);
}

// 约束 T 必须实现 Comparable 接口
public class Sorter<T extends Comparable<T>> {
    public void sort (T[] array) {
        // 简单的冒泡排序
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - i - 1; j++) {
                if (array[j].compare_to (array[j + 1]) > 0) {
                    T temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }
}

// 实现 Comparable 接口
public class Student : Object, Comparable<Student> {
    public string name { get; set; }
    public int score { get; set; }

    public Student (string name, int score) {
        Object (name: name, score: score);
    }

    public int compare_to (Student other) {
        return this.score - other.score;  // 按成绩排序
    }
}

void main () {
    var students = new Student[] {
        new Student ("张三", 85),
        new Student ("李四", 92),
        new Student ("王五", 78),
        new Student ("赵六", 95)
    };

    var sorter = new Sorter<Student> ();
    sorter.sort (students);

    print ("成绩排名:\n");
    foreach (var s in students) {
        print ("  %s: %d\n", s.name, s.score);
    }
}

6.4.3 多重约束

// 多个约束(使用逗号分隔)
public class Processor<T extends Object, Comparable<T>> {
    private GLib.List<T> items = new GLib.List<T> ();

    public void add (T item) {
        items.append (item);
    }

    public T? find_max () {
        if (items.length () == 0) return null;

        T? max = null;
        foreach (var item in items) {
            if (max == null || item.compare_to (max) > 0) {
                max = item;
            }
        }
        return max;
    }
}

6.5 泛型与 GObject

6.5.1 泛型类继承 GObject

// 泛型类继承 Object
public class GenericBox<T> : Object {
    private T _content;

    public T content {
        get { return _content; }
        set { _content = value; }
    }

    public GenericBox (T content) {
        Object ();
        _content = content;
    }
}

// 泛型信号
public class EventEmitter<T> : Object {
    public signal void emitted (T data);

    public void emit_data (T data) {
        emitted (data);
    }
}

void main () {
    // 使用泛型容器
    var box = new GenericBox<string> ("Hello Vala");
    print ("内容: %s\n", box.content);

    // 使用泛型信号
    var emitter = new EventEmitter<int> ();
    emitter.emitted.connect ((data) => {
        print ("收到数据: %d\n", data);
    });
    emitter.emit_data (42);
}

6.5.2 泛型接口

// 泛型接口
public interface Repository<T> {
    public abstract void add (T item);
    public abstract T? find_by_id (int id);
    public abstract GLib.List<T> get_all ();
    public abstract void remove (T item);
}

// 具体实现
public class UserRepository : Object, Repository<User> {
    private GLib.List<User> users = new GLib.List<User> ();
    private int next_id = 1;

    public void add (User user) {
        user.id = next_id++;
        users.append (user);
    }

    public User? find_by_id (int id) {
        foreach (var user in users) {
            if (user.id == id) return user;
        }
        return null;
    }

    public GLib.List<User> get_all () {
        return users;
    }

    public void remove (User user) {
        users.remove (user);
    }
}

public class User : Object {
    public int id { get; set; }
    public string name { get; set; }

    public User (string name) {
        Object (name: name);
    }
}

void main () {
    var repo = new UserRepository ();
    repo.add (new User ("Alice"));
    repo.add (new User ("Bob"));
    repo.add (new User ("Charlie"));

    print ("所有用户:\n");
    foreach (var user in repo.get_all ()) {
        print ("  [%d] %s\n", user.id, user.name);
    }

    var found = repo.find_by_id (2);
    if (found != null) {
        print ("找到用户: %s\n", found.name);
    }
}

6.6 泛型的高级用法

6.6.1 泛型与类型推断

// Vala 可以推断泛型参数
T identity<T> (T value) {
    return value;
}

T[] create_array<T> (T first, T second, T third) {
    T[] arr = new T[3];
    arr[0] = first;
    arr[1] = second;
    arr[2] = third;
    return arr;
}

void main () {
    // 类型推断
    var x = identity<int> (42);           // 显式指定
    var y = identity ("Hello");            // 自动推断为 string
    var z = identity<double> (3.14);      // 显式指定

    print ("x=%d, y=%s, z=%.2f\n", x, y, z);

    // 数组类型推断
    var numbers = create_array<int> (1, 2, 3);
    foreach (var n in numbers) {
        print ("%d ", n);
    }
    print ("\n");
}

6.6.2 泛型与继承

// 泛型基类
public class BaseRepository<T> : Object {
    protected GLib.List<T> items = new GLib.List<T> ();

    public void add (T item) {
        items.append (item);
    }

    public uint size () {
        return items.length ();
    }
}

// 子类指定具体类型
public class StringRepository : BaseRepository<string> {
    public void print_all () {
        foreach (var item in items) {
            print ("  - %s\n", item);
        }
    }
}

// 子类保持泛型
public class CachedRepository<T> : BaseRepository<T> {
    private GLib.HashTable<int, T> cache =
        new GLib.HashTable<int, T> (direct_hash, direct_equal);

    public void cache_item (int key, T item) {
        cache[key] = item;
        add (item);
    }

    public T? get_cached (int key) {
        return cache[key];
    }
}

void main () {
    // 使用具体子类
    var str_repo = new StringRepository ();
    str_repo.add ("Hello");
    str_repo.add ("World");
    print ("字符串仓库 (%u 项):\n", str_repo.size ());
    str_repo.print_all ();

    // 使用泛型子类
    var int_repo = new CachedRepository<int> ();
    int_repo.cache_item (1, 100);
    int_repo.cache_item (2, 200);
    print ("整数仓库 (%u 项)\n", int_repo.size ());
    print ("缓存 key=1: %d\n", int_repo.get_cached (1));
}

6.6.3 泛型约束与 requires

// 使用 requires 子句(Vala 0.56+)
T max_value<T> (T a, T b) requires T: Comparable<T> {
    if (a.compare_to (b) > 0) {
        return a;
    }
    return b;
}

// 复杂约束
public class PriorityQueue<T> : Object
    requires T: Comparable<T>
{
    private T[] heap = new T[0];
    private int _size = 0;

    public int size {
        get { return _size; }
    }

    public void enqueue (T item) {
        // 扩容
        if (_size >= heap.length) {
            int new_length = heap.length == 0 ? 4 : heap.length * 2;
            heap.resize (new_length);
        }
        heap[_size] = item;
        _size++;
        bubble_up (_size - 1);
    }

    public T? dequeue () {
        if (_size == 0) return null;

        T top = heap[0];
        _size--;
        heap[0] = heap[_size];
        bubble_down (0);
        return top;
    }

    private void bubble_up (int index) {
        while (index > 0) {
            int parent = (index - 1) / 2;
            if (heap[index].compare_to (heap[parent]) < 0) {
                T temp = heap[index];
                heap[index] = heap[parent];
                heap[parent] = temp;
                index = parent;
            } else {
                break;
            }
        }
    }

    private void bubble_down (int index) {
        while (true) {
            int smallest = index;
            int left = 2 * index + 1;
            int right = 2 * index + 2;

            if (left < _size &&
                heap[left].compare_to (heap[smallest]) < 0) {
                smallest = left;
            }
            if (right < _size &&
                heap[right].compare_to (heap[smallest]) < 0) {
                smallest = right;
            }

            if (smallest != index) {
                T temp = heap[index];
                heap[index] = heap[smallest];
                heap[smallest] = temp;
                index = smallest;
            } else {
                break;
            }
        }
    }
}

6.7 业务场景:泛型缓存系统

// 缓存项
public class CacheEntry<T> : Object {
    public T value { get; set; }
    public int64 created_at { get; set; }
    public int64 ttl { get; set; }  // 生存时间(毫秒)

    public CacheEntry (T value, int64 ttl) {
        Object ();
        this.value = value;
        this.ttl = ttl;
        this.created_at = GLib.get_real_time () / 1000;
    }

    public bool is_expired () {
        int64 now = GLib.get_real_time () / 1000;
        return (now - created_at) > ttl;
    }
}

// 泛型缓存管理器
public class CacheManager<T> : Object {
    private GLib.HashTable<string, CacheEntry<T>> cache;
    private int max_size;
    private int hit_count = 0;
    private int miss_count = 0;

    public signal void evicted (string key);

    construct {
        cache = new GLib.HashTable<string, CacheEntry<T>> (
            str_hash, str_equal
        );
    }

    public CacheManager (int max_size = 100) {
        Object ();
        this.max_size = max_size;
    }

    public void put (string key, T value, int64 ttl = 60000) {
        // 淘汰过期项
        cleanup ();

        // 如果缓存满了,淘汰最旧的
        if (cache.size () >= max_size) {
            evict_oldest ();
        }

        cache[key] = new CacheEntry<T> (value, ttl);
    }

    public T? get (string key) {
        var entry = cache[key];
        if (entry == null) {
            miss_count++;
            return null;
        }

        if (entry.is_expired ()) {
            cache.remove (key);
            miss_count++;
            return null;
        }

        hit_count++;
        return entry.value;
    }

    public bool contains (string key) {
        return get (key) != null;
    }

    public void remove (string key) {
        cache.remove (key);
    }

    private void cleanup () {
        var keys_to_remove = new GLib.List<string> ();
        cache.foreach ((key, entry) => {
            if (entry.is_expired ()) {
                keys_to_remove.append (key);
            }
        });
        foreach (var key in keys_to_remove) {
            cache.remove (key);
            evicted (key);
        }
    }

    private void evict_oldest () {
        string? oldest_key = null;
        int64 oldest_time = int64.MAX;

        cache.foreach ((key, entry) => {
            if (entry.created_at < oldest_time) {
                oldest_time = entry.created_at;
                oldest_key = key;
            }
        });

        if (oldest_key != null) {
            cache.remove (oldest_key);
            evicted (oldest_key);
        }
    }

    public void print_stats () {
        int total = hit_count + miss_count;
        double hit_rate = total > 0 ? (double) hit_count / total * 100 : 0;
        print ("缓存统计: 大小=%u, 命中=%d, 未命中=%d, 命中率=%.1f%%\n",
               cache.size (), hit_count, miss_count, hit_rate);
    }
}

void main () {
    var cache = new CacheManager<string> (100);

    cache.evicted.connect ((key) => {
        print ("淘汰: %s\n", key);
    });

    // 添加缓存项
    cache.put ("user:1", "Alice", 5000);
    cache.put ("user:2", "Bob", 5000);
    cache.put ("config:db", "localhost:5432", 10000);

    // 获取缓存项
    string? user1 = cache.get ("user:1");
    if (user1 != null) {
        print ("用户1: %s\n", user1);
    }

    string? unknown = cache.get ("user:999");
    print ("未知用户: %s\n", unknown ?? "未找到");

    cache.print_stats ();
}

6.8 注意事项

⚠️ 泛型常见陷阱

  1. 不能对泛型参数使用 sizeof:Vala 泛型不支持编译时大小计算
  2. 值类型有装箱开销:泛型参数是值类型(如 int)时会有装箱开销
  3. 不能使用 new T():不能直接实例化泛型参数类型
  4. 类型检查有限:运行时类型检查在泛型中有限制
  5. 接口约束的限制:不是所有约束都能表达

6.9 扩展阅读

资源链接
Vala 泛型文档https://wiki.gnome.org/Projects/Vala/GenericTypes
Vala 类型系统https://wiki.gnome.org/Projects/Vala/TypeSystem
GLib 数据结构https://docs.gtk.org/glib/data-structures.html
Valadoc 泛型https://valadoc.org/

6.10 总结

要点说明
泛型类class MyClass<T> { }
泛型方法void my_func<T> (T arg) { }
类型约束T extends ObjectT: Comparable<T>
类型推断Vala 可自动推断泛型参数
性能值类型有装箱开销
GObject 兼容泛型类可以继承 Object

下一章我们将学习 Vala 的异步编程。→ 第 7 章:异步编程