抽象类
abstract
是方法的另一个修饰符,用于描述未在类中实际实现的方法。这类方法必须在子类中实现后才能被调用,确保所有子类型都提供自己的功能实现。
包含抽象方法的类必须声明为 abstract
,这会阻止该类型的实例化。
// 抽象类 Animal 继承自 Object
public abstract class Animal : Object {
// 已实现的普通方法
public void eat() {
stdout.printf("*chomp chomp*\n");
}
// 抽象方法(无实现)
public abstract void say_hello();
}
// Tiger 子类实现抽象方法
public class Tiger : Animal {
public override void say_hello() {
stdout.printf("*roar*\n");
}
}
// Duck 子类实现抽象方法
public class Duck : Animal {
public override void say_hello() {
stdout.printf("*quack*\n");
}
}
- 抽象方法的实现必须标记
override
- 属性(property)也可声明为抽象
虚方法
virtual
方法允许在抽象类中定义默认实现,同时允许派生类通过 override
覆盖其行为(与方法隐藏不同)。
// 抽象类 Caller
public abstract class Caller : GLib.Object {
// 抽象属性
public abstract string name { get; protected set; }
// 抽象方法
public abstract void update (string new_name);
// 虚方法(含默认实现)
public virtual bool reset ()
{
name = "No Name";
return true;
}
}
// ContactCV 子类:重写全部抽象成员和虚方法
public class ContactCV : Caller
{
public override string name { get; protected set; }
public override void update (string new_name)
{
name = "ContactCV - " + new_name;
}
// 覆盖虚方法
public override bool reset ()
{
name = "ContactCV-Name";
stdout.printf ("CotactCV.reset () 实现!\n");
return true;
}
}
// Contact 子类:使用虚方法的默认实现
public class Contact : Caller {
public override string name { get; protected set; }
public override void update (string new_name)
{
name = "Contact - " + new_name;
}
// 主函数演示
public static void main ()
{
var c = new Contact ();
c.update ("John Strauss");
stdout.printf(@"Name: $(c.name)\n"); // 输出:Name: Contact - John Strauss
c.reset (); // 调用默认虚方法
stdout.printf(@"Reset Name: $(c.name)\n"); // Reset Name: No Name
var cv = new ContactCV ();
cv.update ("Xochitl Calva");
stdout.printf(@"Name: $(cv.name)\n"); // Name: ContactCV - Xochitl Calva
cv.reset (); // 调用覆盖后的虚方法
stdout.printf(@"Reset Name: $(cv.name)\n"); // Reset Name: ContactCV-Name
stdout.printf("END\n"); // 输出 END
}
}
关键行为
类 | 行为 |
---|---|
Caller | 定义了抽象属性 name 、抽象方法 update 和虚方法 reset (默认实现) |
Contact | 实现抽象成员,但使用 reset() 的默认逻辑 |
ContactCV | 实现所有抽象成员,并覆盖 reset() 的自定义逻辑 |
接口
接口基础
Vala 的类可以实现任意数量的接口。接口是一种类型(类似于类),但不可实例化。通过实现接口,类声明其实例也属于该接口类型,可在需要接口类型的场景中使用。
接口定义与实现
// 定义接口 ITest(需继承 GLib.Object)
public interface ITest : GLib.Object {
public abstract int data_1 { get; set; } // 抽象属性
public abstract void method_1(); // 抽象方法
}
// 实现接口 ITest 的类
public class Test1 : GLib.Object, ITest {
public int data_1 { get; set; } // 必须实现接口属性
public void method_1() { // 必须实现接口方法
// 方法逻辑
}
}
// 使用示例
var t = new Test1();
t.method_1();
ITest i = t; // 通过接口类型操作实例
i.method_1();
接口前置条件
Vala 接口不能继承其他接口,但可声明其他接口或类作为前置条件,强制实现类满足依赖关系。
示例:接口依赖链
// 定义接口 List 的前置条件
public interface List : Collection, Traversable {
// 接口成员
}
// 实现类必须同时实现 Collection 和 List
public class ListClass : GLib.Object, Collection, List {
// 实现所有接口成员
}
类作为前置条件
public interface Callable : GLib.Object { // 强制实现类继承 GLib.Object
// 接口成员
}
public class Phone : GLib.Object, Callable { // 符合要求
// 实现接口
}
接口默认方法实现
Vala 接口支持非抽象方法(需声明为 virtual
),可定义默认实现(类似混入 mixins)。
带默认方法的接口
public interface Callable : GLib.Object {
public abstract bool answering { get; protected set; } // 抽象属性
public abstract void answer(); // 抽象方法
// 默认方法(虚方法)
public virtual bool hang() {
answering = false;
return true;
}
}
使用默认实现
public class Phone : GLib.Object, Callable {
public bool answering { get; protected set; }
public void answer() {
// 接听逻辑
}
public static void main() {
var f = new Phone();
if (f.hang()) { // 调用接口默认方法
stdout.printf("Hang done.\n");
}
}
}
// 输出: Hang done.
当编译并运行以下代码时,Phone
类未显式实现 Callable.hang()
方法,但能调用接口的默认实现,输出结果为 Hang done.
:
public class TechPhone : GLib.Object, Callable
{
public bool answering { get; protected set; }
public void answer() {
/* 接听逻辑实现 */
}
// 完全覆盖默认的 hang()
public bool hang() {
answering = false;
stdout.printf("TechPhone.hang() 实现!\n");
return false;
}
}
- 行为:当
TechPhone
的实例调用hang()
时,完全隐藏了Callable.hang()
的默认实现,始终返回false
并打印自定义消息。
接口属性
接口可定义必须实现的属性。实现类需声明同名属性,并严格匹配访问权限:
// 接口定义
public interface Callable : GLib.Object {
public abstract bool answering { get; protected set; } // 只读对外,可写对内
}
// 实现类
public class TechPhone : GLib.Object, Callable {
private bool _answering; // 私有字段存储属性值
// 显式实现属性存取器
public bool answering {
get { return _answering; }
protected set { _answering = value; }
}
}
- 关键点:若未显式定义属性存取逻辑,Vala 会生成默认实现;显式定义时需手动管理私有字段。
混入与多重继承
Vala 通过接口的虚方法默认实现模拟有限的多重继承(Mixins):
// 接口定义默认方法
public interface Callable : GLib.Object {
public abstract bool answering { get; protected set; }
public abstract void answer();
// 静态方法作为默认实现
public static bool default_hang(Callable call) {
stdout.printf("Callable.hang() 被调用\n");
call.answering = false;
return true;
}
}
// 基类实现接口
public abstract class Caller : GLib.Object, Callable {
public bool answering { get; protected set; }
public void answer() {
stdout.printf("Caller.answer() 被调用\n");
answering = true;
hang();
}
// 虚方法调用接口默认实现
public virtual bool hang() {
return Callable.default_hang(this);
}
}
// 子类 TechPhone 直接继承默认实现
public class TechPhone : Caller {
public string number { get; set; }
}
// 子类 Phone 覆盖虚方法
public class Phone : Caller {
public override bool hang() {
stdout.printf("Phone.hang() 被调用\n");
return false;
}
public static void main() {
var f = (Callable) new Phone();
f.answer(); // 输出: Caller.answer() 被调用 → Phone.hang() 被调用
if (f.hang())
stdout.printf("Hand done.\n"); // 不会执行
else
stdout.printf("Hand Error!\n"); // 输出: Hand Error!
var t = (Callable) new TechPhone();
t.answer(); // 输出: Caller.answer() 被调用 → Callable.hang() 被调用
if (t.hang())
stdout.printf("Tech Hand done.\n"); // 输出: Tech Hand done.
stdout.printf("END\n");
}
}
输出结果
Caller.answer() 被调用
Phone.hang() 被调用
Hand Error!
Caller.answer() 被调用
Callable.hang() 被调用
Tech Hand done.
END
显式方法实现
当实现多个接口存在同名方法冲突时,可通过显式声明解决:
// 定义两个接口
interface Foo {
public abstract int m();
}
interface Bar {
public abstract string m();
}
// 显式实现不同接口的同名方法
class Cls: Foo, Bar {
// Foo 的 m() 实现
public int Foo.m() {
return 10;
}
// Bar 的 m() 实现
public string Bar.m() {
return "bar";
}
}
void main() {
var cls = new Cls();
message("%d %s", ((Foo) cls).m(), ((Bar) cls).m()); // 输出: 10 bar
}
核心规则
- 语法:通过
接口名.方法名()
显式绑定实现。 - 调用:需将对象强制转换为接口类型后调用对应方法。