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 注意事项
⚠️ 泛型常见陷阱
- 不能对泛型参数使用
sizeof:Vala 泛型不支持编译时大小计算 - 值类型有装箱开销:泛型参数是值类型(如
int)时会有装箱开销 - 不能使用
new T():不能直接实例化泛型参数类型 - 类型检查有限:运行时类型检查在泛型中有限制
- 接口约束的限制:不是所有约束都能表达
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 Object 或 T: Comparable<T> |
| 类型推断 | Vala 可自动推断泛型参数 |
| 性能 | 值类型有装箱开销 |
| GObject 兼容 | 泛型类可以继承 Object |
下一章我们将学习 Vala 的异步编程。→ 第 7 章:异步编程