interfaz - pasos para crear un componente en java
La interfaz de Java y la clase de tipos de Haskell: ¿diferencias y similitudes? (10)
Como dice Daniel, las implementaciones de interfaz se definen por separado de las declaraciones de datos. Y como otros han señalado, existe una manera directa de definir operaciones que usan el mismo tipo gratuito en más de un lugar. Entonces, es fácil definir Num
como una clase de tipo. Por lo tanto, en Haskell obtenemos los beneficios sintácticos de la sobrecarga del operador sin tener en realidad operadores mágicamente sobrecargados, solo clases de tipos estándar.
Otra diferencia es que puede usar métodos basados en un tipo, ¡incluso cuando todavía no tiene un valor concreto de ese tipo!
Por ejemplo, read :: Read a => String -> a
. Entonces, si tiene suficiente información de otro tipo acerca de cómo usará el resultado de una "lectura", puede dejar que el compilador averigüe qué diccionario usar para usted.
También puede hacer cosas como instance (Read a) => Read [a] where...
que le permite definir una instancia de lectura para cualquier lista de elementos legibles. No creo que eso sea posible en Java.
Y todo esto es solo clases de tipos estándar de un solo parámetro sin trucos. Una vez que introducimos las clases de parámetros de múltiples parámetros, se abre un nuevo mundo de posibilidades, y más aún con dependencias funcionales y familias de tipos, que le permiten incorporar mucha más información y computación en el sistema de tipos.
Mientras estoy aprendiendo Haskell, noté su clase de tipo , que se supone que es una gran invención que se originó en Haskell.
Sin embargo, en la página de Wikipedia sobre la clase de tipo :
El programador define una clase de tipo especificando un conjunto de nombres de función o constante, junto con sus respectivos tipos, que deben existir para cada tipo que pertenece a la clase.
Lo cual me parece bastante cercano a la interfaz de Java (citando la página de la Interfaz de Wikipedia (Java) ):
Una interfaz en el lenguaje de programación Java es un tipo abstracto que se usa para especificar una interfaz (en el sentido genérico del término) que las clases deben implementar.
Estos dos aspectos son bastante similares: el límite de la clase de tipo es el comportamiento de un tipo, mientras que la interfaz limita el comportamiento de una clase.
Me pregunto cuáles son las diferencias y similitudes entre la clase de tipo en Haskell y la interfaz en Java, o tal vez son fundamentalmente diferentes.
EDIT: noté que incluso haskell.org admite que son similares . Si son tan similares (¿o lo son?), ¿Por qué la clase de tipo se trata con tanta exageración?
MÁS EDITAR: ¡ Guau, tantas buenas respuestas! Creo que tendré que dejar que la comunidad decida cuál es la mejor. Sin embargo, al leer las respuestas, todos parecen decir que "hay muchas cosas que la clase de tipos puede hacer mientras que la interfaz no puede o tiene que hacer frente a los genéricos" . No puedo evitar preguntarme: ¿hay algo que las interfaces puedan hacer mientras que las clases de tipos no pueden? Además, noté que Wikipedia afirma que la clase de tipo fue inventada originalmente en el documento de 1989 * "Cómo hacer un polimorfismo ad-hoc menos ad hoc", mientras que Haskell todavía está en su cuna, mientras que el proyecto Java se inició en 1991 y se lanzó por primera vez en 1995 Entonces, ¿ tal vez en lugar de que typeclass sea similar a interfaces, es al revés, que las interfaces fueron influenciadas por typeclass? ¿Hay documentos / documentos que apoyen o refuten esto? Gracias por todas las respuestas, ¡todas son muy esclarecedoras!
¡Gracias por todas las entradas!
En las mentes maestras de la programación , hay una entrevista sobre Haskell con Phil Wadler, el inventor de las clases de tipos, que explican las similitudes entre las interfaces en Java y las clases de tipos en Haskell:
Un método Java como:
public static <T extends Comparable<T>> T min (T x, T y) { if (x.compare(y) < 0) x; else y; }
es muy similar al método Haskell:
min :: Ord a => a -> a -> a min x y = if x < y then x else y
Por lo tanto, las clases de tipo están relacionadas con las interfaces, pero la correspondencia real sería un método estático parametrizado con un tipo como el anterior.
He leído las respuestas anteriores. Siento que puedo responder un poco más claro:
Una "clase de tipo" de Haskell y una "interfaz" de Java / C # o un "rasgo" de Scala son básicamente análogas. No existe una distinción conceptual entre ellos, pero existen diferencias de implementación:
- Las clases de tipo Haskell se implementan con "instancias" que son independientes de la definición del tipo de datos. En C # / Java / Scala, las interfaces / rasgos deben implementarse en la definición de la clase.
- Las clases de tipo Haskell le permiten devolver este tipo o tipo de uno mismo. Los rasgos de Scala también (este tipo). Tenga en cuenta que los "tipos propios" en Scala son una característica completamente no relacionada. Java / C # requieren una solución desordenada con genéricos para aproximarse a este comportamiento.
- Las clases de tipo Haskell le permiten definir funciones (incluidas las constantes) sin un parámetro de tipo "this" de entrada. Las interfaces Java / C # y Scala requieren un parámetro de entrada "this" en todas las funciones.
- Las clases de tipo Haskell le permiten definir implementaciones predeterminadas para funciones. Lo mismo ocurre con los rasgos Scala y las interfaces Java 8+. C # puede aproximarse a algo como esto con los métodos de extensiones.
Las clases de tipo se crearon como una forma estructurada para expresar el "polimorfismo ad-hoc", que es básicamente el término técnico para las funciones sobrecargadas . Una definición de clase de tipo se ve así:
class Foobar a where
foo :: a -> a -> Bool
bar :: String -> a
Lo que esto significa es que, cuando utilizas la función foo
para algunos argumentos de un tipo que pertenece a la clase Foobar
, busca una implementación de foo
específica para ese tipo, y la usa. Esto es muy similar a la situación de sobrecarga del operador en idiomas como C, excepto que es más flexible y generalizado.
Las interfaces tienen un propósito similar en los lenguajes OO, pero el concepto subyacente es algo diferente; Los lenguajes OO vienen con una noción incorporada de jerarquías de tipo que Haskell simplemente no tiene, lo que complica las cosas de alguna manera porque las interfaces pueden involucrar tanto la sobrecarga mediante subtipificación (es decir, llamar métodos en instancias apropiadas, subtipos implementando interfaces que hacen sus supertipos) y por despacho plano basado en tipos (dado que dos clases que implementan una interfaz pueden no tener una superclase común que también la implemente). Dada la gran complejidad adicional introducida por la subtipificación, sugiero que es más útil pensar en las clases de tipos como una versión mejorada de las funciones sobrecargadas en un idioma que no sea OO.
También vale la pena señalar que las clases de tipo tienen medios de envío mucho más flexibles: las interfaces generalmente se aplican solo a la clase única que las implementa, mientras que las clases de tipo se definen para un tipo que puede aparecer en cualquier lugar de la firma de las funciones de la clase. El equivalente de esto en interfaces OO permitiría a la interfaz definir formas de pasar un objeto de esa clase a otras clases, definir métodos estáticos y constructores que seleccionarían una implementación en función de qué tipo de retorno se requiere en el contexto de llamada, definir métodos que tome argumentos del mismo tipo que la clase que implementa la interfaz, y varias otras cosas que realmente no se traducen en absoluto.
En resumen: sirven para propósitos similares, pero la forma en que funcionan es algo diferente, y las clases de tipos son significativamente más expresivas y, en algunos casos, más simples de usar debido a que trabajan en tipos fijos que en piezas de una jerarquía de herencia.
Lea Extensión de software e integración con clases de tipos donde se dan ejemplos de cómo las clases de tipos pueden resolver una serie de problemas que las interfaces no pueden.
Los ejemplos enumerados en el documento son: el problema de la expresión, el problema de integración del marco, el problema de la extensibilidad independiente, la tiranía de la descomposición dominante, la dispersión y el enredo.
Lo que es similar entre interfaces y clases de tipos es que nombran y describen un conjunto de operaciones relacionadas. Las operaciones mismas se describen a través de sus nombres, entradas y salidas. Asimismo, puede haber muchas implementaciones de estas operaciones que probablemente difieran en su implementación.
Con eso fuera del camino, aquí hay algunas diferencias notables:
- Los métodos de interfaz siempre están asociados con una instancia de objeto. En otras palabras, siempre hay un parámetro ''this'' implícito que es el objeto sobre el que se llama el método. Todas las entradas a una función de clase de tipo son explícitas.
- Una implementación de interfaz debe definirse como parte de la clase que implementa la interfaz. Por el contrario, una "instancia" de clase de tipo se puede definir completamente separada de su tipo asociado ... incluso en otro módulo.
- Una clase de tipo le permite definir una implementación ''predeterminada'' para cualquiera de las operaciones definidas. Las interfaces son estrictamente solo especificaciones de tipo, sin implementación.
En general, creo que es justo decir que las clases de tipos son más potentes y flexibles que las interfaces. ¿Cómo definiría una interfaz para convertir una cadena en algún valor o instancia del tipo de implementación? Ciertamente no es imposible, pero el resultado no sería intuitivo o elegante. ¿Alguna vez deseó que fuera posible implementar una interfaz para un tipo en alguna biblioteca compilada? Ambos son fáciles de lograr con clases de tipos.
Mire la charla de Phillip Wadler Faith, Evolution, and Programming Languages . Wadler trabajó en Haskell y fue un importante colaborador de Java Generics.
No puedo hablarle al nivel de "exageración", si me parece bien. Pero las clases de tipo sí son similares en muchas formas. Una diferencia que puedo pensar es que Haskell puede proporcionar un comportamiento para algunas de las operaciones de la clase de tipo:
class Eq a where
(==), (/=) :: a -> a -> Bool
x /= y = not (x == y)
x == y = not (x /= y)
que muestra que hay dos operaciones, igual (==)
y no igual (/=)
, para cosas que son instancias de la clase de tipo Eq
. Pero la operación no igual se define en términos de iguales (para que solo tenga que proporcionar una), y viceversa.
Entonces, en Java probablemente no legal, sería algo así como:
interface Equal<T> {
bool isEqual(T other) {
return !isNotEqual(other);
}
bool isNotEqual(T other) {
return !isEqual(other);
}
}
y la forma en que funcionaría es que solo necesitaría proporcionar uno de esos métodos para implementar la interfaz. Entonces, diría que la capacidad de proporcionar una especie de implementación parcial del comportamiento que desea en el nivel de interfaz es una diferencia.
Son similares (léase: tienen un uso similar), y probablemente se implementen de manera similar: las funciones polimórficas en Haskell toman bajo el capó un ''vtable'' que enumera las funciones asociadas con la clase de tipo.
Esta tabla a menudo se puede deducir en tiempo de compilación. Esto es probablemente menos cierto en Java.
Pero esta es una tabla de funciones , no de métodos . Los métodos están vinculados a un objeto, las clases de tipos Haskell no lo son.
Véalos como los genéricos de Java.
Diría que una interfaz es algo así como una clase de tipo SomeInterface t
donde todos los valores tienen el tipo t -> whatever
(donde whatever
que no contenga t
). Esto se debe a que con el tipo de relación de herencia en Java y en lenguajes similares, el método llamado depende del tipo de objeto al que se les llama y nada más.
Eso significa que es realmente difícil hacer cosas como add :: t -> t -> t
con una interfaz, donde es polimórfico en más de un parámetro, porque no hay forma de que la interfaz especifique que el tipo de argumento y el tipo de retorno de el método es del mismo tipo que el tipo de objeto al que se llama (es decir, el tipo "self"). Con Generics, hay formas de falsificar esto haciendo una interfaz con un parámetro genérico que se espera que sea del mismo tipo que el objeto en sí, como lo hace Comparable<T>
, donde se espera que utilice Foo implements Comparable<Foo>
para que el tipo compareTo(T otherobject)
tenga el tipo t -> t -> Ordering
. Pero eso aún requiere que el programador siga esta regla, y también causa dolores de cabeza cuando la gente quiere hacer una función que utiliza esta interfaz, tienen que tener parámetros de tipo genéricos recursivos.
Además, no tendrá cosas como empty :: t
porque no está llamando a una función aquí, entonces no es un método.