Vala编程语言教程-抽象类和接口

抽象类

   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
}

核心规则

  • 语法‌:通过 接口名.方法名() 显式绑定实现。
  • 调用‌:需将对象强制转换为接口类型后调用对应方法。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__XYZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值