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的一部分生成的,由ILbox
指令执行。请参阅中的M()
)。可以想象,Haxe可以添加一个功能来自动为您生成这样一个包装类,就像C#为struct
类型所做的那样,但这目前不是一个功能。但是,您可以自己做,如上所示。感谢您的详细解释。我不想使用静态扩展,因为接口应该隐藏从业务逻辑实现内部协议。因此它不应该直接应用于BytesOutput
,因为输出对象可以是XML或JSON格式。我宁愿使用包装类。您不能使用结构子类型来实现吗(在我的示例中,键入内容为int32writeable
而不是BytesOutput
)?然后,也许最好的解决方案还是简单地创建一个子类/包装类。:)就我个人而言,结构子类型和接口之间没有区别。因此我投票支持接口解决方案,认为它更清晰、更健壮。您可以尝试使用隐式类型转换使抽象符合IOutput类型,也许?