
Java es el primer lenguaje que aprendí en mi carrera. Su estructura es fundamental en mis primeros años de comprensión de conceptos de programación. Después de pasar por varios lenguajes con enfoques muy diferentes, he ampliado mi punto de vista. Hoy quiero reflexionar sobre la idea de herencia .
En Java, la idea de herencia está estrechamente relacionada con el concepto de subtipo . El subtipo es la implementación de una relación IS A. Por ejemplo, la clase Rabbit
es un subtipo de la clase Mammal
. De ahí en adelante, una instancia Rabbit
tiene todo el comportamiento de una Mammal
: hereda el comportamiento.
Por este motivo, puedes pasar una instancia Rabbit
cuando un método solicita un parámetro Mammal
o devolver una instancia Rabbit
cuando el tipo de retorno de un método es Mammal
. Si has aprendido Java, .Net o algo remotamente similar, así es como ves la herencia y se convierte en la nueva normalidad.
Es herencia explicita .
class Animal { void feed(); } class Rabbit extends Animal { //1 }
Rabbit
IS A
Animal
, puede feed()
Cuando miré Go por primera vez, me sorprendió que no tuviera subtipos y aun así proporcionara herencia. Go usa tipado de pato:
Si parece un pato, nada como un pato y grazna como un pato, entonces probablemente lo sea.
Si una struct
Go implementa las mismas funciones que una interfaz, implementa implícitamente la interfaz.
type Animal interface { feed() //1 } type Rabbit struct { } func (rabbit Rabbit) feed() { //2 // feeds }
Animal
puede alimentarsefeed()
que toma un Rabbit
como parámetro, Rabbit
implementa Animal
No me gusta Go por su enfoque de manejo de errores, pero tenía dudas sobre la implementación implícita. Por un lado, entendía que era un concepto nuevo y traté de mantener la mente abierta; por otro lado, creo que las cosas siempre son mejores explícitas que implícitas, ya sea en el desarrollo de software o en la vida real.
Python es el lenguaje más interesante que conozco respecto a la herencia.
La subtipificación y la herencia basada en tipos han estado presentes en Python desde sus inicios.
class Animal: def feed(self): #1 pass #2 class Rabbit(Animal): #3 pass
Animal
puede alimentarseRabbit
IS A
Animal
, puede feed()
En este sentido, Python funciona igual que Java en términos de herencia. Python también ofrece tipado de pato, que describí como métodos mágicos . Por ejemplo, para hacer que algo sea iterable , por ejemplo , que pueda devolver un iterador , solo necesitas implementar __iter__()
y __next__()
:
class SingleValueIterable(): done = False def __init__(self, value): self.value = value def __iter__(self): #1 return self def __next__(self): #1 if self.done: raise StopIteration else: self.done = True return self.value svi = SingleValueIterable(5) sviter = iter(svi) #2 for x in sviter: print(x) #3
Iterator
: Python sabe cómo hacerlo ya que implementamos los métodos anteriores5
El problema con este enfoque de tipado de pato es que solo funciona para los métodos mágicos predefinidos de Python. ¿Qué sucede si desea ofrecer una clase de la que un tercero podría heredar de manera implícita?
class Animal: def feed(): pass class Rabbit: def feed(): pass
En el fragmento anterior, Rabbit
no es un Animal
, para nuestro disgusto. Entramos en PEP 544 , titulado Protocolos: subtipificación estructural (tipificación estática de pato). PEP resuelve la imposibilidad de definir métodos mágicos para nuestras clases. Define una clase Protocol
simple: una vez que hereda de ella, los métodos definidos en la clase se vuelven elegibles para la tipificación de pato, de ahí el nombre: tipificación estática de pato.
from typing import Protocol class Animal(Protocol): #1 def feed(): #2 pass class Rabbit: def feed(): #2 pass class VenusFlytrap: def feed(): #2 pass
Protocol
Animal
es un Protocol
, cualquier clase que defina feed()
se convierte en un Animal
, para bien o para mal.La programación orientada a objetos, la herencia y la subtipificación pueden tener significados específicos que no se traducen a otros lenguajes, según el primer lenguaje que aprendas. Java se promociona como un lenguaje orientado a objetos y ofrece el paquete completo. Go no es un lenguaje orientado a objetos, pero aun así ofrece subtipificación mediante tipado de pato. Python ofrece herencia explícita e implícita, pero no interfaces.
Aprendes un nuevo lenguaje de programación comparándolo con los que ya conoces. Conocer las características de un lenguaje es fundamental para escribir código idiomático en el lenguaje de destino. Familiarízate con las características que no existen en los lenguajes que conoces: ampliarán tu comprensión de la programación en su conjunto.
Ir más allá:
Publicado originalmente en A Java Geek el 26 de enero de 2025