Interface 抽象如何实现接口?

Interface 抽象如何实现接口?,interface,abstract,haxe,implements,Interface,Abstract,Haxe,Implements,我有一个公共接口,描述对输出流的访问,如下所示: interface IOutput { function writeInteger(aValue:Int):Void; } class Main { public static function out(aOutput:IOutput) { aOutput.writeInteger(0); } public static function main() { var output:C

我有一个公共接口,描述对输出流的访问,如下所示:

interface IOutput {
    function writeInteger(aValue:Int):Void;
}
class Main {
    public static function out(aOutput:IOutput) {
        aOutput.writeInteger(0);
    }
    public static function main() {
        var output:COutput = new BytesOutput();
        out(output); // type error
    }
}
我有一个基于标准
haxe.io.BytesOutput
类的抽象接口实现:

abstract COutput(BytesOutput) from BytesOutput {
    public inline function new(aData:BytesOutput) {
        this = aData;
    }
    public inline function writeInteger(aValue:Int):Void {
        this.writeInt32(aValue);
    }
}
虽然这个抽象真正实现了上面描述的接口,但并没有直接引用接口,当我尝试这样使用它时:

interface IOutput {
    function writeInteger(aValue:Int):Void;
}
class Main {
    public static function out(aOutput:IOutput) {
        aOutput.writeInteger(0);
    }
    public static function main() {
        var output:COutput = new BytesOutput();
        out(output); // type error
    }
}
编译器抛出错误:
COutput应该是IOutput
。我只能通过使用包装
BytesOutput
并实现
IOutput
的公共类来解决这个问题

我的问题是如何向Haxe编译器显示抽象实现了接口。

抽象是因为它们是编译时特性,在运行时不存在。这与接口冲突,它们确实存在于运行时,像
Std.is(something,IOutput)
这样的动态运行时检查必须工作

Haxe还有一种称为的机制,可以用作接口的替代方案。使用这种方法,不需要显式的
实现
声明,如果具有以下结构的东西:

typedef IOutput = {
    function writeInteger(aValue:Int):Void;
}
不幸的是,抽象要么是因为它们的实现方式


你有没有考虑改用它?至少对于您的简单示例而言,这似乎是使
writeInteger()
方法可用于任何以下对象的完美解决方案:

您甚至可以将其与结构子类型相结合,以便
writeinger()
可以用于任何具有
writeing32()
方法()的对象:

作为。在Haxe中,为了实现一个接口,类型必须能够被编译成这样的类,可以使用接口的方法调用,而不会发生任何奇妙的事情。也就是说,要使用类型作为接口,需要有一个实现该类型的“真实”类。Haxe中的摘要编译到它们的基类型摘要本身在编译之后是完全不可见的。因此,在运行时,没有一个类的实例具有在抽象中定义的实现接口的方法

但是,您可以通过与您试图实现的接口进行交互,使抽象看起来实现了一个接口。对于您的示例,以下方法可能有效:

interface IOutput {
    function writeInteger(aValue:Int):Void;
}

abstract COutput(BytesOutput) from BytesOutput {
    public inline function new(aData:BytesOutput) {
        this = aData;
    }
    @:to()
    public inline function toIOutput():IOutput {
        return new COutputWrapper((cast this : COutput));
    }
    public inline function writeInteger(aValue:Int):Void {
        this.writeInt32(aValue);
    }
}

class COutputWrapper implements IOutput {
    var cOutput(default, null):COutput;
    public function new(cOutput) {
        this.cOutput = cOutput;
    }
    public function writeInteger(aValue:Int) {
        cOutput.writeInteger(aValue);
    }
}

class Main {
    public static function out(aOutput:IOutput) {
        aOutput.writeInteger(0);
    }
    public static function main() {
        var output:COutput = new BytesOutput();
        out(output);
        out(output);
    }
}

请注意,每次发生隐式转换时,都将构造包装器的新实例。这可能会影响性能。如果只通过接口访问您的值,请考虑将变量的类型设置为接口而不是抽象。
这类似于C#中的“装箱”原语/值类型。在C#中,允许使用
struct
关键字定义值类型来实现接口。与Haxe中的抽象一样,C#中的值类型(通过抖动)被编译成非类型化代码,该代码直接访问和操作特定操作的值。但是,C#允许
struct
s实现接口。C#编译器将把任何试图隐式地将
结构转换为实现的接口的尝试转换为包装类的构造,该包装类存储值的副本,并实现与手动编写的包装类类似的接口(这个包装类实际上是由运行时作为JIT的一部分生成的,由IL
box
指令执行。请参阅中的
M()
)。可以想象,Haxe可以添加一个功能来自动为您生成这样一个包装类,就像C#为
struct
类型所做的那样,但这目前不是一个功能。但是,您可以自己做,如上所示。

感谢您的详细解释。我不想使用静态扩展,因为接口应该隐藏从业务逻辑实现内部协议。因此它不应该直接应用于
BytesOutput
,因为输出对象可以是XML或JSON格式。我宁愿使用包装类。您不能使用结构子类型来实现吗(在我的示例中,键入内容为
int32writeable
而不是
BytesOutput
)?然后,也许最好的解决方案还是简单地创建一个子类/包装类。:)就我个人而言,结构子类型和接口之间没有区别。因此我投票支持接口解决方案,认为它更清晰、更健壮。您可以尝试使用隐式类型转换使抽象符合IOutput类型,也许?