mapa - programacion orientada a objetos polimorfismo java
¿Es posible el polimorfismo sin herencia? (6)
En una entrevista, me preguntaron si el polimorfismo se puede lograr sin herencia. es posible?
La mejor explicación sobre el tema que he leído es un artículo de Luca Cardelli , un teórico de renombre. El artículo se llama Sobre tipos de comprensión, abstracción de datos y polimorfismo .
Tipos de polimorfismo
Cardelli define varios tipos de polimorfismo en este artículo:
- Universal
- paramétrico
- inclusión
- Ad hoc
- sobrecarga
- coerción
El tipo de polimorfismo relacionado con la herencia se clasifica como polimorfismo de inclusión o subtipo de polimorfismo.
Wikipedia proporciona una buena definición:
En la programación orientada a objetos, el polimorfismo de subtipo o polimorfismo de inclusión es un concepto en la teoría de tipos en el que un nombre puede denotar instancias de muchas clases diferentes siempre que estén relacionadas por alguna superclase común. El polimorfismo de inclusión generalmente se admite a través de la subtipificación, es decir, los objetos de diferentes tipos son totalmente sustituibles por objetos de otro tipo (su (s) tipo (s) base (s)) y, por lo tanto, pueden manejarse a través de una interfaz común. Alternativamente, el polimorfismo de inclusión se puede lograr a través de la coerción de tipo, también conocido como tipo de molde.
Otro artículo de Wikipedia llamado Polimorfismo en la programación orientada a objetos parece responder a sus preguntas también.
En Java
Esta característica de subtipificación en Java se logra, entre otros medios, a través de la herencia de clases e interfaces. Aunque las características de subtipado de Java pueden no ser evidentes en términos de herencia todo el tiempo. Tomemos, por ejemplo, los casos de covarianza y contravarianza con genéricos. Además, las matrices son serializables y clonables, aunque esto no es evidente en ninguna parte de la jerarquía de tipos. También se puede decir que a través de la conversión primitiva de ensanchamiento los operadores numéricos en Java son polimórficos, en ciertos casos incluso aceptan operandos totalmente no relacionados (es decir, concatenación de cadenas y números o de una cadena más algún otro objeto). Considere también los casos de boxeo y desempaquetado de primitivas. Estos últimos casos de polimorfismo (coacciones y sobrecarga) no están relacionados en absoluto con la herencia.
Ejemplos
Inclusión
List<Integer> myInts = new ArrayList<Integer>();
Este es el caso al que parece referirse su pregunta, es decir, cuando existe una relación de herencia o implementación entre los tipos, como en este caso donde ArrayList implementa List.
Como mencioné, sin embargo, cuando introduces los genéricos de Java, algunas veces las reglas de subtipado se vuelven borrosas:
List<? super Number> myObjs = new ArrayList<Object>();
List<? extends Number> myNumbers = new LinkedList<Integer>();
Y en otros casos, las relaciones ni siquiera son evidentes en la API
Cloneable clone = new int[10];
Serializable obj = new Object[10]
Aun así, todo esto, según Cardelli, son formas de polimorfismo universal.
Paramétrico
public <T> List<T> filter(Predicate<T> predicate, List<T> source) {
List<T> result = new ArrayList<>();
for(T item : source) {
if(predicate.evaluate(item)){
result.add(item);
}
return result;
}
}
El mismo algoritmo se puede utilizar para filtrar todo tipo de listas con todos los predicados de clases sin tener que repetir una sola línea de código para cada tipo de lista posible. El tipo de la lista real y el tipo de predicado son paramétricos. Vea este ejemplo con expresiones lambda disponibles en JDK 8 Preview (para la brevedad de la implementación del predicado).
filter(x -> x % 2 == 0, asList(1,2,3,4,5,6)); //filters even integers
filter(x -> x % 2 != 0, asList(1L,2L,3L,4L,5L,6L)); //filters odd longs
filter(x -> x >= 0.0, asList(-1.0, 1.0)); //filters positive doubles
Según Cardelli, esta es una forma de polimorfismo universal.
Coerción
double sum = 1 + 2.0;
La aritmética de enteros y coma flotante es totalmente diferente. Aplicar el operador más a dos operandos de diferentes tipos aquí es imposible sin alguna forma de coerción.
En este ejemplo, los tipos entero y doble, se fuerzan automáticamente (se convierten) para escribir el doble sin un molde explícito. La expresión completa se promueve a doble. Esto es así porque en Java tenemos conversiones primarias de ampliación.
Según Cardelli, esta forma de coerción automática es una forma de polimorfismo ad-hoc proporcionada para el operador plus.
Hay idiomas en los que ni siquiera se puede sumar un número entero y un número de punto flotante sin un molde explícito (es decir, AFAIK, SML, en el que, por cierto, el polimorfismo paramétrico es clave para superar este tipo de problemas).
Sobrecarga
double sum = 2.0 + 3.0;
String text = "The sum is" + sum;
El operador más aquí significa dos cosas diferentes según los argumentos utilizados. Evidentemente, el operador se ha sobrecargado. Esto implica que tiene implementaciones diferentes según los tipos de operandos. Según Cardelli, esta es una forma de polimorfismo ad-hoc proporcionado para el operador plus.
Esto, por supuesto, también se aplica a las formas de sobrecarga de métodos en las clases (es decir, los métodos java.lang.Math min y max están sobrecargados para admitir diferentes tipos primitivos).
En otros idiomas
Incluso cuando la herencia desempeña un papel importante en la implementación de algunas de estas formas de polimorfismo, ciertamente no es la única forma. Otros lenguajes que no están orientados a objetos proporcionan otras formas de polimorfismo. Tomemos, por ejemplo, los casos de tipado de patos en lenguajes dinámicos como Python o incluso en lenguajes de tipo estático como Go, o tipos de datos algebraicos en idiomas como SML, Ocaml y Scala, o clases de tipos en idiomas como Haskell, multi métodos en Clojure , herencia prototípica en JavaScript, etc.
La sobrecarga de funciones es uno de los polimorfismos (aunque no es lo que se entiende por polimorfismo real) que se puede lograr sin herencia.
p.ej
class Foo {
public void Arrest( Animal A){
/*code...*/
}
public void Arrest( Terrorist T ) {
/*code...*/
}
}
from main :
Foo f= new Foo();
f.Arrest( new Lion() );
f.Arrest(new Terrorist());
El método de detención se llama 2 veces pero la ruta de ejecución del código es diferente.
* De nuevo, esta no es una forma verdadera de polimorfismo. El polimorfismo real en general no se puede lograr sin herencia.
Polimorfismo Ad-hoc> Sobrecarga del operador> Sin herencia
Polimorfismo Ad-hoc> Método de sobrecarga> Sin herencia
Polimorfismo Ad-hoc> Anulación de método> Con herencia
Polimorfismo paramétrico> Genéricos> Sin herencia
Subtipo de polimorfismo o polimorfismo de inclusión> Asignación polimórfica> Con herencia
Polimorfismo de subtipo o polimorfismo de inclusión> Tipo de retorno polimórfico> Con herencia
Polimorfismo de subtipo o polimorfismo de inclusión> Tipo de argumento polimórfico> Con herencia
Polimorfismo de coerción> Ensanchamiento> Con o sin herencia
Polimorfismo de coerción> Auto boxing y unboxing> Sin herencia
Polimorfismo de coerción> Var args> Sin herencia
Polimorfismo de coerción> Tipo de casting> Sin herencia
Por supuesto. En Java, puede hacer que dos clases implementen la misma interfaz, y sus resultados son polimórficos. Ninguna funcionalidad es heredada.
public interface Foo {
public int a();
}
public class A implements Foo {
public int a() {
return 5;
}
}
public class B implements Foo {
public int a() {
return 6;
}
}
Luego en otro lugar:
Foo x = new A();
System.out.println(x.a())
Foo y = new B();
System.out.println(y.a())
Tanto x
como y
son Foo
, pero tienen resultados diferentes cuando se llama a()
.
Sí, creo que probablemente querían saber sobre el polimorfismo por interfaces. Entonces, si hay 2 clases que implementa desde la misma interfaz, entonces podemos usar en todos los lugares donde esperamos un objeto con tal intervalo. Ver código de wikipedia:
// from file Animal.java
public interface Animal {
public String talk();
}
// from file Cat.java
public class Cat implements Animal {
@Override
public String talk() {
return "Cat says Meow!";
}
}
// from file Dog.java
public class Dog implements Animal {
@Override
public String talk() {
return "Dog says Woof! Woof!";
}
}
// from file PolymorphismExample.java
public class PolymorphismExample {
public static void main(String[] args) {
Collection<Animal> animals = new ArrayList<Animal>();
animals.add(new Cat());
animals.add(new Dog());
for (Animal a : animals) {
System.out.println(a.talk());
}
}
}
Tipo estático
sobrecarga, lo que significa que hay varios métodos con el mismo nombre pero con una firma diferente, que es posible sin anular
class StaticPolyExample
{
void print(int s)
{
//print s
}
void print(String s)
{
//print s
}
}
Tipo dinámico
overriding - lo que significa que el método en superclase se redefinirá en la subclase que necesita herencia
class Printer
{
void print(String s)
{
// prints String
}
}
class diffPrinter extends Printer
{
void print(String s)
{
// prints String differently
}
}