Para los idiomas contemporáneos, es solo azúcar sintáctico; de una manera completamente independiente del lenguaje, es más que eso.
Anteriormente, esta respuesta decía simplemente que es más que azúcar sintáctica, pero si lo verá en los comentarios, Falco planteó el punto de que había una pieza del rompecabezas que los lenguajes contemporáneos parecen faltar; no mezclan la sobrecarga de métodos con la determinación dinámica de qué función llamar en el mismo paso. Esto se aclarará más adelante.
He aquí por qué debería ser más.
Considere un lenguaje que admita sobrecarga de métodos y variables sin tipo. Podría tener los siguientes prototipos de métodos:
bool someFunction(int arg);
bool someFunction(string arg);
En algunos idiomas, es probable que se resigne a saber en tiempo de compilación a cuál de estos se llamaría mediante una línea de código determinada. Pero en algunos idiomas, no todas las variables se escriben (o todas se escriben implícitamente como Object
o lo que sea), así que imagine construir un diccionario cuyas claves correspondan con valores de diferentes tipos:
dict roomNumber; // some hotels use numbers, some use letters, and some use
// alphanumerical strings. In some languages, built-in dictionary
// types automatically use untyped values for their keys to map to,
// so it makes more sense then to allow for both ints and strings in
// your code.
Ahora bien, ¿qué pasaría si quisieras postular someFunction
a uno de esos números de habitación? A esto le llamas:
someFunction(roomNumber[someSortOfKey]);
¿Se someFunction(int)
llama o se someFunction(string)
llama? Aquí puede ver un ejemplo en el que estos no son métodos totalmente ortogonales, especialmente en lenguajes de nivel superior. El lenguaje tiene que descubrir, durante el tiempo de ejecución, a cuál de estos llamar, por lo que aún debe considerar que estos son al menos el mismo método.
¿Por qué no simplemente usar plantillas? ¿Por qué no simplemente usar un argumento sin tipo?
Flexibilidad y control de grano más fino. A veces, usar plantillas / argumentos sin tipo es un mejor enfoque, pero a veces no lo son.
Debe pensar en casos en los que, por ejemplo, podría tener dos firmas de métodos que toman un int
y un string
como argumentos, pero donde el orden es diferente en cada firma. Es muy posible que tenga una buena razón para hacerlo, ya que la implementación de cada firma puede hacer en gran medida lo mismo, pero con un giro ligeramente diferente; el registro podría ser diferente, por ejemplo. O incluso si hacen exactamente lo mismo, es posible que pueda recopilar automáticamente cierta información solo en el orden en que se especificaron los argumentos. Técnicamente, podría usar declaraciones de seudoconmutador para determinar el tipo de cada uno de los argumentos pasados, pero eso se vuelve desordenado.
Entonces, ¿este siguiente ejemplo es una mala práctica de programación?
bool stringIsTrue(int arg)
{
if (arg.toString() == "0")
{
return false;
}
else
{
return true;
}
}
bool stringIsTrue(Object arg)
{
if (arg.toString() == "0")
{
return false;
}
else
{
return true;
}
}
bool stringIsTrue(string arg)
{
if (arg == "0")
{
return false;
}
else
{
return true;
}
}
Sí, en general. En este ejemplo particular, podría evitar que alguien intente aplicar esto a ciertos tipos primitivos y recupere comportamientos inesperados (lo que podría ser algo bueno); pero supongamos que abrevié el código anterior y que, de hecho, usted tiene sobrecargas para todos los tipos primitivos, así como para Object
s. Entonces este siguiente fragmento de código es realmente más apropiado:
bool stringIsTrue(untyped arg)
{
if (arg.toString() == "0")
{
return false;
}
else
{
return true;
}
}
Pero, ¿qué sucede si solo necesita usar esto para int
sy string
s, y qué pasa si desea que se vuelva verdadero en función de condiciones más simples o más complicadas en consecuencia? Entonces tiene una buena razón para usar la sobrecarga:
bool appearsToBeFirstFloor(int arg)
{
if (arg.digitAt(0) == 1)
{
return true;
}
else
{
return false;
}
}
bool appearsToBeFirstFloor(string arg)
{
string firstCharacter = arg.characterAt(0);
if (firstCharacter.isDigit())
{
return appearsToBeFirstFloor(int(firstCharacter));
}
else if (firstCharacter.toUpper() == "A")
{
return true;
}
else
{
return false;
}
}
Pero oye, ¿por qué no solo dar a esas funciones dos nombres diferentes? Todavía tienes la misma cantidad de control de grano fino, ¿no?
Porque, como se indicó anteriormente, algunos hoteles usan números, algunos usan letras y otros usan una combinación de números y letras:
appearsToBeFirstFloor(roomNumber[someSortOfKey]);
// will treat ints and strings differently, without you having to write extra code
// every single spot where the function is being called
Este todavía no es exactamente el mismo código exacto que usaría en la vida real, pero debería ilustrar el punto que estoy haciendo bien.
Pero ... He aquí por qué no es más que azúcar sintáctica en los idiomas contemporáneos.
Falco planteó el punto en los comentarios de que los lenguajes actuales básicamente no mezclan la sobrecarga de métodos y la selección dinámica de funciones dentro del mismo paso. La forma en que anteriormente entendía que ciertos idiomas funcionaban era que se podía sobrecargarappearsToBeFirstFloor
en el ejemplo anterior, y luego el idioma determinaría en tiempo de ejecución qué versión de la función se llamará, dependiendo del valor de tiempo de ejecución de la variable sin tipo. Esta confusión se debió en parte al trabajar con lenguajes de tipo ECMA, como ActionScript 3.0, en el que puede aleatorizar fácilmente qué función se llama en una determinada línea de código en tiempo de ejecución.
Como ya sabrás, ActionScript 3 no admite la sobrecarga de métodos. En cuanto a VB.NET, puede declarar y establecer variables sin asignar un tipo explícitamente, pero cuando intenta pasar estas variables como argumentos a métodos sobrecargados, aún no desea leer el valor de tiempo de ejecución para determinar qué método llamar; en su lugar, quiere encontrar un método con argumentos de tipo Object
o sin tipo o algo por el estilo. Por lo que el int
vs. string
ejemplo anterior no funcionaría en ese idioma tampoco. C ++ tiene problemas similares, ya que cuando usa algo como un puntero nulo o algún otro mecanismo como ese, aún requiere que desambigue manualmente el tipo en tiempo de compilación.
Entonces, como dice el primer encabezado ...
Para los idiomas contemporáneos, es solo azúcar sintáctico; de una manera completamente independiente del lenguaje, es más que eso. Hacer que la sobrecarga de métodos sea más útil y relevante, como en el ejemplo anterior, en realidad puede ser una buena característica para agregar a un lenguaje existente (como se ha solicitado ampliamente implícitamente para AS3), o también podría servir como uno entre muchos pilares fundamentales diferentes para la creación de un nuevo lenguaje procesal / orientado a objetos.