13.14. Herencia y polimorfismo en Java: principio de sustitución de Liskov
La programación orientada a objetos (POO) es un paradigma de programación que utiliza "objetos" para modelar datos y comportamiento. Java, al ser un lenguaje orientado a objetos, permite a los programadores aprovechar conceptos como herencia y polimorfismo para crear programas más flexibles y reutilizables. Profundicemos nuestra comprensión de estos conceptos y exploremos el Principio de sustitución de Liskov (LSP), uno de los principios fundamentales de la programación orientada a objetos.
Herencia en Java
La herencia es un mecanismo mediante el cual una nueva clase, llamada subclase, puede heredar campos y métodos de otra clase, llamada superclase. Esto permite que las subclases reutilicen y amplíen el comportamiento de las superclases. En Java, la herencia se realiza con la palabra clave extends
.
clase Vehículo { movimiento vacío público() { System.out.println("Vehículo en movimiento"); } } clase Coche extiende Vehículo { @Anular movimiento vacío público() { System.out.println("Coche en movimiento"); } }
En el ejemplo anterior, Car
es una subclase de Vehicle
. La subclase Car
hereda el método move()
y proporciona su propia implementación utilizando la anotación @Override
. Esto ilustra la extensión y reutilización de la funcionalidad.
Polimorfismo en Java
El polimorfismo es la capacidad de un objeto de ser tratado como una instancia de su propia clase o de cualquier clase de la que herede. En Java, esto se logra comúnmente mediante la anulación y sobrecarga de métodos.
Con la anulación de métodos, una subclase puede proporcionar una implementación específica de un método que ya existe en su superclase. La sobrecarga de métodos permite que varios métodos tengan el mismo nombre, pero con diferentes listas de parámetros.
Principio de sustitución de Liskov (LSP)
El Principio de Sustitución de Liskov, formulado por Barbara Liskov en 1987, es un concepto fundamental en la programación orientada a objetos. LSP establece que si S
es un subtipo de T
, entonces los objetos de tipo T
en un programa pueden ser reemplazados por objetos de tipo S
sin cambiar las propiedades deseables de ese programa. En otras palabras, una subclase debe ser reemplazable por su superclase sin causar errores.
Para unirse a LSP, una subclase debe:
- No cambie el comportamiento esperado de los métodos de superclase.
- No violar las invariantes y contratos establecidos por la superclase.
- Asegúrese de que los subtipos se puedan utilizar para reemplazar los tipos base sin necesidad de conocer la diferencia entre ellos.
La violación de LSP puede provocar errores y comportamientos inesperados en un programa. Por ejemplo:
clase pájaro { mosca vacía pública () { System.out.println("Pájaro volando"); } } clase Pingüino extiende Pájaro { @Anular mosca vacía pública () { throw new UnsupportedOperationException("Los pingüinos no vuelan"); } }
En el ejemplo anterior, Penguin
es una subclase de Passaro
, pero anula el método fly()
para generar una excepción, como Los pingüinos no vuelan. Esto viola LSP porque el comportamiento de la superclase Bird
se cambia de una manera que puede causar errores si los objetos Penguin
se usan en contextos que esperan que todos los pájaros puedan volar.
Cómo aplicar LSP en Java
Para garantizar que se respete LSP en Java, siga estas pautas:
- Evite anular métodos de manera que cambie el comportamiento esperado.
- Utilice la anotación
@Override
cuando anule métodos para asegurarse de que realmente está modificando un método de superclase. - Cuando sea necesario, utilice la composición en lugar de la herencia para compartir comportamientos entre clases que no tienen una relación de sustituibilidad.
- Respetar los contratos establecidos por la superclase, incluidas las precondiciones, poscondiciones e invariantes.
La adopción de LSP ayuda a mantener la integridad del diseño orientado a objetos, promoviendo la creación de sistemas más robustos y fáciles de mantener.
Conclusión
La herencia y el polimorfismo son conceptos poderosos en Java que, cuando se usan correctamente, pueden aumentar la reutilización y la flexibilidad del código. El principio de sustitución de Liskov es esencial para utilizar estos conceptos de forma eficaz, garantizando que las subclases se puedan utilizar en lugar de sus superclases sin causar efectos secundarios no deseados. Al diseñar e implementar sus clases de Java, tenga siempre en cuenta LSP para crear un código más consistente.fiable y fácil de mantener.