Can I specify interfaces when I declare a member?
在考虑了这个问题一段时间之后,我发现静态鸭式语言可能确实有效。为什么在编译时不能将预定义的类绑定到接口?例如:
public interface IMyInterface
{
public void MyMethod();
}
public class MyClass //Does not explicitly implement IMyInterface
{
public void MyMethod() //But contains a compatible method definition
{
Console.WriteLine("Hello, world!");
}
}
...
public void CallMyMethod(IMyInterface m)
{
m.MyMethod();
}
...
MyClass obj = new MyClass();
CallMyMethod(obj); // Automatically recognize that MyClass "fits"
// MyInterface, and force a type-cast.
您知道支持此功能的任何语言吗?它会在Java或C#中有用吗?它在某种程度上存在根本缺陷吗?我知道你可以继承MyClass并实现接口或使用Adapter设计模式来完成同样的事情,但这些方法看似不必要的样板代码。
答案 0 :(得分:18)
这个问题的全新答案Go has exactly this feature。我认为这真的非常酷聪明(虽然我有兴趣看看它在现实生活中是如何发挥作用的)并且在想到它时会感到荣幸。
记录在案in the official documentation (as part of the Tour of Go, with example code):
接口是隐式实现的
类型通过实现其方法来实现接口。有 没有明确的意图声明,没有“implements”关键字。
隐式接口将接口的定义与其分离 实现,然后可以出现在没有的任何包中 预先安排。
答案 1 :(得分:13)
如何在C ++中使用模板?
class IMyInterface // Inheritance from this is optional
{
public:
virtual void MyMethod() = 0;
}
class MyClass // Does not explicitly implement IMyInterface
{
public:
void MyMethod() // But contains a compatible method definition
{
std::cout << "Hello, world!" "\n";
}
}
template<typename MyInterface>
void CallMyMethod(MyInterface& m)
{
m.MyMethod(); // instantiation succeeds iff MyInterface has MyMethod
}
MyClass obj;
CallMyMethod(obj); // Automatically generate code with MyClass as
// MyInterface
我实际上并没有编译这段代码,但我相信它是可行的,并且是原始提议(但非工作)代码的一个非常简单的C ++化。
答案 2 :(得分:10)
我没有看到这一点。为什么不明确该类实现接口并完成它?实现接口是告诉其他程序员该类应该以接口定义的方式运行的原因。简单地在方法上具有相同的名称和签名并不能保证设计者的意图是使用该方法执行类似的操作。这可能是,但为什么要留下来解释(和误用)?
你可以在动态语言中成功“逃脱”的原因更多地与TDD有关,而不是与语言本身有关。在我看来,如果该语言提供了向使用/查看代码的其他人提供这些指导的工具,那么您应该使用它。它实际上提高了清晰度,值得一些额外的角色。如果您无权执行此操作,则适配器的用途与明确声明接口与其他类的关联方式相同。
答案 3 :(得分:10)
静态类型语言,根据定义,在编译时检查类型,而不是运行时。上述系统的一个明显问题是编译器将在编译程序时检查类型,而不是在运行时。
现在,您可以在编译器中构建更多智能,以便派生类型,而不是让程序员明确声明类型;编译器可能会看到MyClass
实现了MyMethod()
方法,并相应地处理了这种情况,没有需要显式声明接口(如你所建议的那样)。这样的编译器可以使用类型推断,例如Hindley-Milner。
当然,像Haskell这样的静态类型语言已经按照你的建议做了类似的; Haskell编译器能够推断类型(大多数时候)而无需显式声明它们。但显然,Java / C#没有这种能力。
答案 4 :(得分:9)
F#支持静态鸭子类型,但有一个catch:你必须使用成员约束。详情请参阅此blog entry。
引用博客中的示例:
let inline speak (a: ^a) =
let x = (^a : (member speak: unit -> string) (a))
printfn "It said: %s" x
let y = (^a : (member talk: unit -> string) (a))
printfn "Then it said %s" y
type duck() =
member x.speak() = "quack"
member x.talk() = "quackity quack"
type dog() =
member x.speak() = "woof"
member x.talk() = "arrrr"
let x = new duck()
let y = new dog()
speak x
speak y
答案 5 :(得分:4)
ML系列中的大多数语言都支持具有推理和约束类型方案的结构类型,这是一种令人讨厌的语言设计术语,似乎很可能是“静态鸭子”这个词的意思。在原始问题中键入“。
这个家庭中更受欢迎的语言包括:Haskell,Objective Caml,F#和Scala。当然,与你的例子最匹配的那个是Objective Caml。以下是您的示例的翻译:
open Printf
class type iMyInterface = object
method myMethod: unit
end
class myClass = object
method myMethod = printf "Hello, world!"
end
let callMyMethod: #iMyInterface -> unit = fun m -> m#myMethod
let myClass = new myClass
callMyMethod myClass
注意:您使用的某些名称必须更改为符合OCaml的标识符案例语义概念,否则,这是一个非常直接的翻译。
另外值得注意的是,callMyMethod
函数中的类型注释和iMyInterface
类类型的定义都不是必需的。目标Caml可以在没有任何类型声明的情况下推断示例中的所有内容。
答案 6 :(得分:4)
答案 7 :(得分:2)
Boo绝对是一种静态鸭式语言:http://boo.codehaus.org/Duck+Typing
摘录:
Boo是一种静态类型语言, 像Java或C#。这意味着你的嘘声 应用程序将以尽可能快的速度运行 用其他静态类型编码的那些 .NET或Mono的语言。但是使用 有时是一种静态类型的语言 把你限制在一个不灵活的地方 详细的编码风格,随着 有时必要的类型声明 (比如“x as int”,但事实并非如此 由于boo的类型通常是必要的 推论)有时是必要的 类型转换(请参阅转换类型)。嘘的 支持类型推断和 最终仿制药在这里有所帮助,但是......
有时放弃是合适的 静电提供的安全网 打字。也许你只是想探索 一个API而不用担心太多 方法签名或者你也许 创建与外部对话的代码 COM对象等组件。或 选择应该是你的选择 矿。
与普通类型一样 object,int,string ... boo有一个 特殊类型称为“鸭子”。术语 灵感来自红宝石编程 语言的鸭子打字功能(“如果它 像鸭子一样走路,像傻瓜一样嘎嘎叫 鸭子,它一定是鸭子“)。
答案 8 :(得分:2)
Scala中的结构类型做了类似的事情。
答案 9 :(得分:2)
新版本的C ++朝着静态鸭子打字的方向发展。你可以在某一天(今天?)写下这样的东西:
auto plus(auto x, auto y){
return x+y;
}
如果x+y
没有匹配的函数调用,则无法编译。
至于你的批评:
一个新的&#34; CallMyMethod&#34;是为您传递给它的每种不同类型创建的,因此它不是真正的类型推断。
但它是类型推断(你可以说foo(bar)
其中foo
是模板化函数),并且具有相同的效果,除了它更节省时间并占用更多空间编译后的代码。
否则,您必须在运行时查找该方法。您必须找到一个名称,然后检查该名称是否具有使用正确参数的方法。
或者您必须存储有关匹配接口的所有信息,并查看与接口匹配的每个类,然后自动添加该接口。
在任何一种情况下,这都允许您隐式地和意外地破坏类层次结构,这对于新功能是不利的,因为它违背了C#/ Java的程序员习惯的习惯。使用C ++模板,您已经知道自己在雷区中(并且他们还添加了功能(&#34;概念&#34;)以允许对模板参数进行限制)。
答案 10 :(得分:1)
Visual Basic 9的预发布设计支持使用动态接口进行静态鸭子输入,但是他们切断了功能*以便按时发货。
答案 11 :(得分:1)
D(http://dlang.org)是一种静态编译的语言,通过wrap()和unwrap()(http://dlang.org/phobos-prerelease/std_typecons.html#.unwrap)提供duck-typing。
答案 12 :(得分:1)
def add(x, y)
x + y
end
add(true, false)
对add
的调用导致此编译错误:
Error in foo.cr:6: instantiating 'add(Bool, Bool)'
add(true, false)
^~~
in foo.cr:2: undefined method '+' for Bool
x + y
^
答案 13 :(得分:0)
听起来像Mixins或Traits:
http://en.wikipedia.org/wiki/Mixin
http://www.iam.unibe.ch/~scg/Archive/Papers/Scha03aTraits.pdf
答案 14 :(得分:0)
在我的编程语言Heron的最新版本中,它通过名为as
的结构 - 子类型强制运算符支持类似的东西。所以而不是:
MyClass obj = new MyClass();
CallMyMethod(obj);
你会写:
MyClass obj = new MyClass();
CallMyMethod(obj as IMyInterface);
就像在您的示例中一样,在这种情况下,MyClass
不必显式实现IMyInterface
,但是如果它确实可以隐式发生强制转换,则as
运算符可以省略。
我写了一些关于这种技术的文章,我在this article中称之为显式结构子类型。