【far 语言】接口与泛型

本文是「far 语言」系列文档之一,总目录见《【far 语言】语言文档目录》。

接口与泛型

Far v2 类型系统:强类型 + 泛型(编译期擦除)+ 接口 + 单继承。组合与继承并存,组合仍是默认倾向。

接口

interface Runnable {
    long run(long x);
    string describe();
}
  • 显式实现:类必须用 impl 声明所实现的接口,方法签名须覆盖接口全部方法;漏写或签名不符都会编译报错。
interface IError { }

class MyError impl IError { }
  • 一个类可以实现多个接口:class MyError impl IError, ILoggable { }
  • 接口变量可接受任意实现类的实例,调用走动态分派(方法表)。
  • 接口之间也可以继承(可多继承,接口无状态): interface IChild : IParentA, IParentB { }

只能写签名

接口方法只有签名,不能写方法体(far 没有 Java 的 default method)。写了会编译报错并指向 { 的位置:

interface Greeter {
    string hello();            // ✅ 正确
    // string hi(){ ... }      // ❌ 接口方法不能有方法体(far 不支持默认方法)
}

实现类的实现必须写在类里:

interface Greeter { string hello(); }

class Dog impl Greeter {
    string hello(){ return "woof"; }   // 实现在类里
}

接口继承与满足性

被 impl 接口的父接口自动被满足 —— 与 Java 的 interface extends 语义一致:

interface Named { string name(); }
interface Aged : Named { long age(); }

class Dog impl Aged {
    string name(){ return "dog"; }
    long age(){ return 3; }
}

Named n = new Dog();     // ✅ Dog impl Aged,而 Aged : Named ⇒ Dog 天然满足 Named
Aged  a = new Dog();     // ✅
Named n2 = a;            // ✅ 接口 → 父接口,同样成立

注意实现类仍需实现继承链上的全部方法(上例 Dog 同时实现了 name()age())。

同名不同参

接口内可声明同名、参数个数不同的方法,调用按实参个数解析:

interface Greeter {
    string greet(string who);
    string greet();
}

class Dog impl Greeter {
    string greet(string who){ return "hi " + who; }
    string greet(){ return "hi"; }
}

Greeter g = new Dog();
g.greet("mike");   // hi mike
g.greet();         // hi

类继承

只允许单继承,语法用冒号;多能力请走 impl 接口列表:

class Animal {
    long hp = 100;
    string speak(){ return "animal"; }
}

class Dog : Animal impl Runnable {
    @Override
    string speak(){ return super.speak() + "+woof"; }
}
  • class 子类 : 父类 impl 接口1, 接口2 { }
  • 父类字段初始化器会自动执行;子类实例调用父类方法天经地义。
  • 子类字段不得与父类同名(编译报错)。
  • super.method() 显式调用父类实现;@Override 断言真覆盖,签名写错立刻报错。
  • 父类变量可持子类实例(向上转型),调用点按运行期类型分派。
  • 详见 inherit.md

泛型类

class Box<T> {
    T val;
    T get(){ return this.val; }
    void set(T v){ this.val = v; }
}

Box<long> b = new Box();
b.set(42);
println(b.get());        // 42
  • 编译期擦除:运行时无类型信息,Box<long>Box 生成相同代码,零开销。
  • 泛型只做编译期类型检查。

泛型约束

class NumberBox<T extends Number> {}
<T extends Runnable> void run(T r) {}
  • 泛型方法实参类型需满足 <T extends Bound> 上界,不满足编译报错。

标准库泛型容器

List<long> nums = new List();          // 见 list.md
map<string, long> m = {"alice": 30};    // 见 map.md

组合与继承

两者不是互斥替代关系,按场景选:

// ① 组合:拥有关系 / 想避免继承耦合(默认倾向)
class Animal {
    long hp;
}

class Dog {
    Animal animal = new Animal();     // 组合:拥有一个 Animal
    long hp(){ return this.animal.hp; }  // 转发
}

// ② 继承:明确的 IS-A 关系 + 需要多态分派(只允许单继承)
class Cat : Animal {
    long lives = 9;
}
  • inherit 关键字不使用;继承语法是冒号 class 子类 : 父类 { }
  • 组合耦合更松、更易替换,是默认倾向;继承用于确实存在 IS-A 与多态需求的场景。
posted @ 2026-09-13 05:05  方东信  阅读(4)  评论(0)    收藏  举报