Pregunta

¿Puedo especificar interfaces cuando declaro un miembro? ?

Después de pensar en esta pregunta por un tiempo, se me ocurrió que un lenguaje de tipo estático podría funcionar. ¿Por qué las clases predefinidas no pueden vincularse a una interfaz en tiempo de compilación? Ejemplo:

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.

¿Conoces algún idioma que admita dicha función? ¿Sería útil en Java o C #? ¿Es fundamentalmente defectuoso de alguna manera? Entiendo que podría subclasificar MyClass e implementar la interfaz o utilizar el patrón de diseño del Adaptador para lograr lo mismo, pero esos enfoques parecen un código repetitivo innecesario.

¿Fue útil?

Solución

Los idiomas de tipo estático, por definición, verifican los tipos en tiempo de compilación , no en tiempo de ejecución . Uno de los problemas obvios con el sistema descrito anteriormente es que el compilador verificará los tipos cuando se compila el programa, no en tiempo de ejecución.

Ahora, podría construir más inteligencia en el compilador para que pueda derivar tipos, en lugar de que el programador explícitamente declare tipos; el compilador puede ver que MyClass implementa un método MyMethod () y maneja este caso en consecuencia, sin la necesidad de declarar explícitamente interfaces (como sugieres). Dicho compilador podría utilizar la inferencia de tipos, como Hindley-Milner .

Por supuesto, algunos lenguajes de tipo estático como Haskell ya hacen algo similar a lo que sugiere; el compilador de Haskell puede inferir tipos (la mayoría de las veces) sin la necesidad de declararlos explícitamente. Pero obviamente, Java / C # no tiene esta capacidad.

Otros consejos

Una nueva respuesta a esta pregunta, Go tiene exactamente esta característica . Creo que es realmente genial & amp; inteligente (aunque me interesará ver cómo se desarrolla en la vida real) y felicitaciones por pensar en ello.

Como se documenta en la documentación oficial (como parte del Tour of Go, con código de ejemplo) :

  

Las interfaces se implementan implícitamente

     

Un tipo implementa una interfaz implementando sus métodos. Ahi esta   sin declaración explícita de intenciones, no '' implementos '' palabra clave.

     

Las interfaces implícitas desacoplan la definición de una interfaz de su   implementación, que luego podría aparecer en cualquier paquete sin   arreglo previo.

¿Qué tal usar plantillas en 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

En realidad no he compilado este código, pero creo que es viable y una C ++ bastante trivial del código original propuesto (pero no funcional).

No veo el punto. ¿Por qué no ser explícito que la clase implementa la interfaz y ha terminado con ella? La implementación de la interfaz es lo que le dice a otros programadores que se supone que esta clase debe comportarse de la manera en que la interfaz define. Simplemente tener el mismo nombre y firma en un método no transmite garantías de que la intención del diseñador fuera realizar acciones similares con el método. Eso puede ser, pero ¿por qué dejarlo para interpretación (y mal uso)?

La razón por la que puedes '' escaparte '' con esto con éxito en lenguajes dinámicos tiene más que ver con TDD que con el lenguaje en sí. En mi opinión, si el lenguaje ofrece la posibilidad de brindar este tipo de orientación a otras personas que usan / ven el código, debe usarlo. Realmente mejora la claridad y vale la pena los pocos caracteres adicionales. En el caso de que no tenga acceso para hacer esto, un adaptador sirve para el mismo propósito de declarar explícitamente cómo se relaciona la interfaz con la otra clase.

F # admite la escritura estática de pato, aunque con una captura: debe usar restricciones de miembros. Los detalles están disponibles en este entrada de blog .

Ejemplo del blog citado:

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

La mayoría de los idiomas de la familia ML admiten tipos estructurales con inferencia y esquemas de tipos restringidos , que es la terminología geek del diseñador de idiomas que parece más probable que entiendas por la frase " pato estático -tipo " en la pregunta original.

Los idiomas más populares en esta familia que se te ocurren incluyen: Haskell, Objective Caml, F # y Scala. El que más se asemeja a su ejemplo, por supuesto, sería Objective Caml. Aquí hay una traducción de su ejemplo:

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

Nota: algunos de los nombres que usó deben cambiarse para cumplir con la noción de semántica de identificador de OCaml, pero de lo contrario, esta es una traducción bastante sencilla.

Además, vale la pena señalar que ni la anotación de tipo en la función callMyMethod ni la definición del tipo de clase iMyInterface son estrictamente necesarias. Objetivo Caml puede inferir todo en su ejemplo sin ninguna declaración de tipo.

TypeScript!

Bueno, está bien ... Por lo tanto, es un superconjunto de JavaScript y tal vez no constituya un "lenguaje", pero este tipo de tipeo estático es vital en TypeScript.

ingrese la descripción de la imagen aquí

Boo definitivamente es un lenguaje estático de tipo pato: http://boo.codehaus.org/Duck + Escribir

Un extracto:

  

Boo es un lenguaje de tipo estático,   como Java o C #. Esto significa tu abucheo   las aplicaciones se ejecutarán tan rápido como   los codificados en otros mecanografiados estáticamente   lenguajes para .NET o Mono. Pero usando   un lenguaje mecanografiado estáticamente a veces   te obliga a un inflexible y   estilo de codificación detallado, con el   declaraciones de tipo a veces necesarias   (como '' x como int '', pero esto no es   a menudo necesario debido al tipo de boo   Inferencia) y a veces necesario   tipos de yeso (ver Tipos de fundición). Boo   soporte para inferencia de tipos y   eventualmente los genéricos ayudan aquí, pero ...

     

A veces es apropiado renunciar   la red de seguridad proporcionada por static   mecanografía. Tal vez solo quieras explorar   una API sin preocuparse demasiado por   firmas de método o tal vez eres   creando código que habla con externos   componentes como los objetos COM. Ya sea   forma en que la elección debe ser tuya no   mio.

     

Junto con los tipos normales como   object, int, string ... boo tiene un   tipo especial llamado "pato". El termino   está inspirado en la programación ruby   función de escritura de pato del idioma (" Si es   camina como un pato y grazna como un   pato, debe ser un pato ").

Las nuevas versiones de C ++ se mueven en la dirección del tipeo de pato estático. Algún día (¿hoy?) Puedes escribir algo como esto:

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

y no se compilaría si no hay una llamada de función coincidente para x + y .

En cuanto a su crítica:

  

Un nuevo " CallMyMethod " se crea para cada tipo diferente que le pasa, por lo que no es realmente inferencia de tipos.

Pero es inferencia de tipo (puede decir foo (bar) donde foo es una función con plantilla), y tiene el mismo efecto, excepto que es más eficiente en el tiempo y ocupa más espacio en el código compilado.

De lo contrario, tendría que buscar el método durante el tiempo de ejecución. Tendría que encontrar un nombre, luego verificar que el nombre tenga un método con los parámetros correctos.

O tendrías que almacenar toda esa información sobre las interfaces coincidentes, y buscar en cada clase que coincida con una interfaz, luego agregar automáticamente esa interfaz.

En cualquier caso, eso le permite romper implícita y accidentalmente la jerarquía de clases, lo cual es malo para una nueva característica porque va en contra de los hábitos a los que los programadores de C # / Java están acostumbrados. Con las plantillas C ++, ya sabes que estás en un campo minado (y también están agregando características (" conceptos ") para permitir restricciones en los parámetros de la plantilla).

Los tipos estructurales en Scala hacen algo como esto.

Ver Comprobado estáticamente" Duck Typing "en Scala

Un diseño de versión preliminar para Visual Basic 9 tenía soporte para la escritura estática de patos usando interfaces dinámicas, pero cortaron la característica * para enviar a tiempo.

D ( http://dlang.org ) es un lenguaje compilado estáticamente y proporciona escritura en pato a través de wrap () and unwrap () ( http://dlang.org/phobos-prerelease/std_typecons.html # .unwrap ).

Crystal es un lenguaje de tipo estático. Ejemplo :

def add(x, y)
  x + y
end

add(true, false)

La llamada a add causa este error de compilación:

Error in foo.cr:6: instantiating 'add(Bool, Bool)'

add(true, false)
^~~

in foo.cr:2: undefined method '+' for Bool

  x + y
    ^

En la última versión de mi lenguaje de programación Heron admite algo similar a través de una coerción de subtipo estructural operador llamado as . Entonces, en lugar de:

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

Escribirías:

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

Al igual que en su ejemplo, en este caso MyClass no tiene que implementar explícitamente IMyInterface , pero si lo hiciera, la conversión podría ocurrir implícitamente y el como El operador podría omitirse.

Escribí un poco más sobre la técnica que llamo subtipo estructural explícito en este artículo .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top