我有一个通用的接口,描述对输出流的访问,如下所示:
const request = require('request');
var size =0;
var maxLength = 8175;
new Promise ((resolve,reject)=>{
var jo = request("https://upload.wikimedia.org/wikipedia/commons/9/9b/Sternwarte_Darmstadt_HDR_Panorama_10MB_-_Photographed_by_James_Breitenstein.jpg")
.on('response',(ok)=>{
jo.responseHeaders = ok.headers;})
.on('data',(ok)=>{
size+=ok.length;
if (size > maxLength){
console.log("DATA",size);resolve(jo.response);jo.abort();}})
.on('end',function(ok){console.log("END");})})
.then(function (ok) {
console.log("ok then",ok)
})
.catch(function (err) {
console.log("error",err)
});
我有一个基于标准interface IOutput {
function writeInteger(aValue:Int):Void;
}
类的接口的抽象实现:
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);
}
}
编译器将引发错误: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 should be IOutput
并实现BytesOutput
的通用类来解决此问题。
我的问题是如何向Haxe编译器表明抽象实现了接口。
答案 0 :(得分:6)
摘要can't implement interfaces是因为它们是编译时功能,并且在运行时不存在。这与接口冲突,它们在运行时存在,并且必须运行Std.is(something, IOutput)
之类的动态运行时检查。
Haxe还具有一种称为structural subtyping的机制,可以用作接口的替代方法。使用这种方法,不需要显式的implements
声明,只要unifies具有以下结构就足够了:
typedef IOutput = {
function writeInteger(aValue:Int):Void;
}
不幸的是,由于aren't compatible with structural subtyping的实现方式,它们都是抽象的。
您是否考虑过改用static extensions?至少对于您的简单示例而言,这似乎是使writeInteger()
方法可用于任何haxe.io.Output
的完美解决方案:
import haxe.io.Output;
import haxe.io.BytesOutput;
using Main.OutputExtensions;
class Main {
static function main() {
var output = new BytesOutput();
output.writeInteger(0);
}
}
class OutputExtensions {
public static function writeInteger(output:Output, value:Int):Void {
output.writeInt32(value);
}
}
您甚至可以将其与结构子类型结合使用,这样writeInteger()
就可以在具有writeInt32()
方法(try.haxe link)的任何事物上使用:
typedef Int32Writable = {
function writeInt32(value:Int):Void;
}
答案 1 :(得分:0)
为@Gama11 states,abstracts cannot implement interfaces。在Haxe中,要使用类型来实现接口,必须能够将其编译为类似类的东西,并且可以使用接口的方法进行调用,而不会发生任何魔术。也就是说,要使用类型作为其接口,需要有一个实现该类型的“真实”类。 Haxe中的摘要会编译为基本类型-编译发生后,摘要本身是完全不可见的。因此,在运行时,没有类的实例具有抽象中定义的实现接口的方法。
不过,您可以通过defining an implicit conversion到要尝试实现的接口,使抽象的 appear 出现以实现接口。对于您的示例,以下方法可能有效:
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#中的值类型(通过JITter)被编译为未类型化的代码,该代码仅直接访问和操纵某些操作的值。但是,C#允许struct
实现接口。 C#编译器会将任何将struct
隐式转换为已实现的接口的尝试转换为构造包装类的结构,该包装类存储值的副本并实现该接口,类似于我们手动编写的包装类(此包装类实际上是由运行时作为JITing的一部分生成的,并由IL box
指令执行。请参见this example中的M()
)。可以想象,Haxe可以添加一个功能来自动为您生成此类包装器类,就像C#对struct
类型所做的那样,但这不是当前的功能。但是,您可以自己进行操作,如上所示。