18

声明成员时可以指定接口吗?

在考虑了这个问题一段时间后,我突然想到静态鸭子类型的语言可能真的有效。为什么预定义的类不能在编译时绑定到接口?例子:

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 设计模式来完成同样的事情,但这些方法看起来像是不必要的样板代码。

4

15 回答 15

19

这个问题的全新答案,Go 正是这个特性。我认为这真的很酷很聪明(尽管我很想看看它在现实生活中是如何发挥作用的)并且对它的思考感到很荣幸。

官方文档中所述(作为围棋之旅的一部分,带有示例代码)

接口是隐式实现的

类型通过实现其方法来实现接口。没有明确的意图声明,没有“implements”关键字。

隐式接口将接口的定义与其实现分离,然后可以在没有预先安排的情况下出现在任何包中。

于 2009-11-11T20:16:44.493 回答
14

在 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++ 化。

于 2008-11-23T21:21:52.527 回答
10

我不明白这一点。为什么不明确说明该类实​​现了接口并完成了它?实现接口就是告诉其他程序员这个类应该按照接口定义的方式运行。仅在方法上具有相同的名称和签名并不能保证设计者的意图是使用该方法执行类似的操作。可能是这样,但为什么要把它留给解释(和滥用)呢?

您可以在动态语言中成功“摆脱”这一点的原因更多地与 TDD 有关,而不是与语言本身有关。在我看来,如果该语言能够为使用/查看代码的其他人提供此类指导,那么您应该使用它。它实际上提高了清晰度,值得添加几个额外的字符。在您无权执行此操作的情况下,适配器的作用与显式声明接口与其他类的关系方式相同。

于 2008-11-14T03:09:41.767 回答
10

根据定义,静态类型语言在编译时检查类型,而不是在运行时检查类型。上述系统的一个明显问题是编译器将在程序编译时检查类型,而不是在运行时检查。

现在,您可以在编译器中构建更多智能,以便它可以派生类型,而不是让程序员显式声明类型;编译器可能能够看到它MyClass实现了一个MyMethod()方法,并相应地处理这种情况,无需显式声明接口(如您所建议的那样)。这样的编译器可以利用类型推断,例如Hindley-Milner

当然,一些像 Haskell 这样的静态类型语言已经做了一些类似于你建议的事情;Haskell 编译器能够(大多数时候)推断类型,而无需显式声明它们。但显然,Java/C# 不具备这种能力。

于 2008-11-14T03:37:54.033 回答
9

F# 支持静态鸭子类型,但有一个问题:您必须使用成员约束。此博客条目中提供了详细信息。

引用博客中的示例:

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
于 2008-11-23T20:47:43.810 回答
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是绝对必要的。Objective Caml 可以推断您示例中的所有内容,而无需任何类型声明。

于 2009-10-24T03:28:03.177 回答
4

打字稿!

好吧,好吧......所以它是一个 javascript 超集,可能不构成一种“语言”,但这种静态鸭子类型在 TypeScript 中是至关重要的。

在此处输入图像描述

于 2014-11-27T17:02:04.670 回答
2

Boo 绝对是一种静态的鸭式语言:http ://boo.codehaus.org/Duck+Typing

摘录:

Boo 是一种静态类型语言,如 Java 或 C#。这意味着您的 boo 应用程序的运行速度与使用 .NET 或 Mono 的其他静态类型语言编写的应用程序一样快。但是使用静态类型语言有时会将您限制在不灵活和冗长的编码风格中,有时需要使用类型声明(例如“x as int”,但由于 boo 的类型推断,这通常不是必需的)和有时需要的类型转换(参见铸造类型)。Boo 对类型推断和最终泛型的支持在这里有所帮助,但是......

有时放弃静态类型提供的安全网是合适的。也许您只是想探索一个 API 而不必过多担心方法签名,或者您正在创建与外部组件(如 COM 对象)对话的代码。无论哪种方式,选择都应该是你的,而不是我的。

除了像 object、int、string...boo 这样的普通类型之外,还有一种特殊的类型,称为“duck”。该术语的灵感来自于 ruby​​ 编程语言的鸭子类型功能(“如果它像鸭子一样走路和像鸭子一样嘎嘎叫,那它一定是鸭子”)。

于 2008-11-14T03:38:23.173 回答
2

Scala 中的结构类型就是这样做的。

请参阅Scala 中的静态检查“鸭子类型”

于 2008-12-28T23:05:10.223 回答
2

新版本的 C++ 朝着静态鸭子类型的方向发展。你可以有一天(今天?)写这样的东西:

auto plus(auto x, auto y){
    return x+y;
}

如果没有匹配的函数调用,它将无法编译x+y

至于你的批评:

为您传递给它的每种不同类型创建一个新的“CallMyMethod”,因此它并不是真正的类型推断。

但它是类型推断(您可以说模板函数foo(bar)在哪里foo),并且具有相同的效果,除了它更省时并且在编译代码中占用更多空间。

否则,您将不得不在运行时查找该方法。您必须找到一个名称,然后检查该名称是否具有具有正确参数的方法。

或者,您必须存储所有有关匹配接口的信息,并查看与接口匹配的每个类,然后自动添加该接口。

在任何一种情况下,这都允许您隐式且意外地破坏类层次结构,这对新功能不利,因为它违背了 C#/Java 程序员的习惯。使用 C++ 模板,您已经知道自己处于雷区(并且他们还添加了功能(“概念”)以允许对模板参数进行限制)。

于 2014-05-09T06:24:09.907 回答
2

Crystal是一种静态鸭式语言。示例

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
    ^
于 2016-05-31T22:53:06.613 回答
1

Visual Basic 9 的预发布设计支持使用动态接口的静态鸭子类型,但他们削减了功能*以便按时发布。

于 2008-11-14T03:36:18.373 回答
1

D ( http://dlang.org ) 是一种静态编译语言,通过 wrap() 和 unwrap() ( http://dlang.org/phobos-prerelease/std_typecons.html#.unwrap ) 提供鸭式输入。

于 2014-04-15T07:48:50.927 回答
0

听起来像 Mixins 或 Traits:
http ://en.wikipedia.org/wiki/Mixin
http://www.iam.unibe.ch/~scg/Archive/Papers/Scha03aTraits.pdf

于 2008-12-28T22:55:54.623 回答
0

在我的编程语言Heron的最新版本中,它通过一个名为as. 所以而不是:

MyClass obj = new MyClass();
CallMyMethod(obj);

你会写:

MyClass obj = new MyClass();
CallMyMethod(obj as IMyInterface);

就像在您的示例中一样,在这种情况下MyClass不必显式实现IMyInterface,但如果确实如此,则强制转换可能会隐式发生,并且as可以省略运算符。

我在本文中写了更多关于我称之为显式结构子类型的技术。

于 2009-12-22T20:23:26.283 回答