Вопрос

Могу ли я указать интерфейсы при объявлении члена?

Подумав некоторое время над этим вопросом, мне пришло в голову, что язык со статической утиной типизацией действительно может работать.Почему предопределенные классы не могут быть привязаны к интерфейсу во время компиляции?Пример:

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 и реализовать интерфейс или использовать шаблон проектирования адаптера для достижения той же цели, но эти подходы кажутся ненужным шаблонным кодом.

Это было полезно?

Решение

Статически типизированные языки по определению проверяют типы на время компиляции, нет время выполнения.Одна из очевидных проблем описанной выше системы заключается в том, что компилятор будет проверять типы при компиляции программы, а не во время ее выполнения.

Теперь вы можете добавить больше интеллекта в компилятор, чтобы он мог вывести типы, вместо того, чтобы программист явно объявить типы;компилятор может это увидеть MyClass реализует MyMethod() метод и обработать этот случай соответствующим образом, без необходимость явного объявления интерфейсов (как вы предлагаете).Такой компилятор может использовать вывод типа, например Хиндли-Милнер.

Конечно, некоторые статически типизированные языки, такие как Haskell, уже что-то делают. похожий к тому, что вы предлагаете;Компилятор Haskell способен выводить типы (в большинстве случаев) без необходимости их явного объявления.Но очевидно, что Java/C# не имеет такой возможности.

Другие советы

Совершенно новый ответ на этот вопрос, Go имеет именно эту функцию.Я думаю, что это действительно круто и умно (хотя мне будет интересно посмотреть, как это будет происходить в реальной жизни), и я благодарен за то, что об этом подумал.

Как документировано в официальной документации (в рамках Tour of Go, с примером кода):

Интерфейсы реализованы неявно

Тип реализует интерфейс, реализуя его методы.Нет явного объявления о намерениях, нет ключевого слова «реализации».

Неявные интерфейсы отделяют определение интерфейса из его реализации, которая затем может появиться в любом пакете без предварительного выбора.

Как насчет использования шаблонов в 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++-версию исходного предложенного (но нерабочего) кода.

Я не вижу в этом смысла.Почему бы не указать явно, что класс реализует интерфейс и покончил с этим?Реализация интерфейса — это то, что сообщает другим программистам, что этот класс должен вести себя так, как определяет интерфейс.Наличие одного и того же имени и подписи в методе не дает никаких гарантий того, что намерением разработчика было выполнение аналогичных действий с этим методом.Возможно, это так, но зачем оставлять это для интерпретации (и неправильного использования)?

Причина, по которой вам это может успешно «избежать» в динамических языках, больше связана с TDD, чем с самим языком.По моему мнению, если язык предлагает возможность давать такого рода рекомендации другим, кто использует/просматривает код, вам следует его использовать.Это действительно улучшает ясность и стоит нескольких дополнительных символов.В случае, когда у вас нет доступа для этого, адаптер служит той же цели: явно объявляет, как интерфейс связан с другим классом.

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

Поддержка большинства языков семейства 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 может вывести все в вашем примере вообще без каких-либо объявлений типов.

Машинопись!

Ладно, хорошо...Таким образом, это надмножество JavaScript и, возможно, не представляет собой «язык», но этот вид статического набора текста жизненно важен в TypeScript.

enter image description here

Boo определенно является статическим языком с утиной типизацией: http://boo.codehaus.org/Duck+Typing

Отрывок:

Бу - это статически напечатанный язык, такой как Java или C#.Это означает, что ваши приложения BOO будут работать примерно так же быстро, как и те, которые кодировались на других статически напечатанных языках для .NET или Mono.Но использование статически напечатанного языка иногда ограничивает вас в негибком и словесном стиле кодирования, с иногда необходимыми объявлениями типа (например, «x как int», но это не часто необходимо из -за вывода типа Boo), а иногда и необходимых типов (см. Типы литья).Поддержка BOO для вывода типа и, в конечном итоге, здесь помогает дженерики, но ...

Иногда целесообразно отказаться от сети безопасности, предоставленной статическим набором.Может быть, вы просто хотите исследовать API, не беспокоясь о подписях метода или, возможно, вы создаете код, который общается с внешними компонентами, такими как COM -объекты.В любом случае выбор должен быть вашим, а не моим.

Наряду с нормальными типами, такими как объект, int, String ... Boo имеет специальный тип, называемый «утка».Термин вдохновлен функцией печати утки Ruby Programing Languing («если он ходит как утка и кряки, как утка, это должна быть утка»).

Новые версии C++ движутся в направлении статической утиной типизации.Вы можете когда-нибудь (сегодня?) написать что-то вроде этого:

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

и он не сможет скомпилироваться, если для него не будет соответствующего вызова функции. x+y.

Что касается вашей критики:

Новый «CallMyMethod» создается для каждого передаваемого ему типа, поэтому на самом деле это не вывод типа.

Но это вывод типа ЕСТЬ (можно сказать foo(bar) где foo является шаблонной функцией) и имеет тот же эффект, за исключением того, что он более эффективен по времени и занимает больше места в скомпилированном коде.

В противном случае вам придется искать метод во время выполнения.Вам нужно будет найти имя, а затем проверить, что это имя имеет метод с правильными параметрами.

Или вам пришлось бы хранить всю информацию о соответствующих интерфейсах и просматривать каждый класс, соответствующий интерфейсу, а затем автоматически добавлять этот интерфейс.

В любом случае это позволяет вам неявно и случайно нарушить иерархию классов, что плохо для новой функции, поскольку противоречит привычкам, к которым привыкли программисты C#/Java.Используя шаблоны C++, вы уже знаете, что находитесь на минном поле (и они также добавляют функции («концепции»), позволяющие налагать ограничения на параметры шаблона).

Структурные типы в Scala делают что-то подобное.

Видеть Статически проверенная «утиная типизация» в Scala

Предварительная версия Visual Basic 9 поддерживала статическую утиную типизацию с использованием динамических интерфейсов, но эта функция была урезана.* чтобы отправить вовремя.

Д (http://dlang.org) — статически компилируемый язык, обеспечивающий утиную типизацию с помощью функций Wrap() и Unwrap() (http://dlang.org/phobos-prerelease/std_typecons.html#.unwrap).

Кристалл — это статически типизированный язык. Пример:

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
    ^

В последней версии моего языка программирования Цапля он поддерживает нечто подобное через оператор приведения структурного подтипа, называемый as.Итак, вместо:

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

Вы бы написали:

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

Как и в вашем примере, в этом случае MyClass не обязательно явно реализовывать IMyInterface, но если бы это было так, приведение могло бы произойти неявно, и as оператор можно опустить.

Я написал немного больше о методе, который я называю явным структурным подтипированием в Эта статья.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top