Para idiomas contemporâneos, é apenas açúcar sintático; de uma maneira completamente independente da linguagem, é mais do que isso.
Anteriormente, essa resposta dizia simplesmente que é mais do que açúcar sintático, mas se você ver nos comentários, Falco levantou a questão de que havia uma peça do quebra-cabeça que as línguas contemporâneas parecem estar faltando; eles não misturam sobrecarga de método com determinação dinâmica de qual função chamar na mesma etapa. Isso será esclarecido mais tarde.
Aqui é por isso que deve haver mais.
Considere uma linguagem que suporte sobrecarga de método e variáveis não tipadas. Você pode ter os seguintes protótipos de método:
bool someFunction(int arg);
bool someFunction(string arg);
Em alguns idiomas, você provavelmente se resignaria a saber, em tempo de compilação, qual deles seria chamado por uma determinada linha de código. Mas em alguns idiomas, nem todas as variáveis são digitadas (ou todas implicitamente digitadas como Object
ou o que for), então imagine criar um dicionário cujas chaves sejam mapeadas para 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.
Agora, então, e se você quisesse aplicar someFunction
a um desses números de quarto? Você chama isso:
someFunction(roomNumber[someSortOfKey]);
É someFunction(int)
chamado ou é someFunction(string)
chamado? Aqui você vê um exemplo em que esses métodos não são totalmente ortogonais, especialmente em idiomas de nível superior. A linguagem precisa descobrir - durante o tempo de execução - qual delas chamar, por isso ainda precisa considerá-las como sendo pelo menos um pouco o mesmo método.
Por que não simplesmente usar modelos? Por que não usar simplesmente um argumento não digitado?
Flexibilidade e controle mais refinado. Às vezes, usar modelos / argumentos sem tipo é uma abordagem melhor, mas às vezes não.
Você deve pensar nos casos em que, por exemplo, você pode ter duas assinaturas de método que usam cada um int
e a string
como argumentos, mas onde a ordem é diferente em cada assinatura. Você pode muito bem ter um bom motivo para fazer isso, pois a implementação de cada assinatura pode fazer basicamente a mesma coisa, mas com um toque um pouco diferente; o registro pode ser diferente, por exemplo. Ou mesmo se eles fizerem exatamente a mesma coisa, você poderá coletar automaticamente determinadas informações apenas pela ordem em que os argumentos foram especificados. Tecnicamente, você pode usar apenas instruções pseudo-switch para determinar o tipo de cada um dos argumentos passados, mas isso fica confuso.
Então, este próximo exemplo é uma prática ruim de programação?
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;
}
}
Sim, em geral. Neste exemplo em particular, isso poderia impedir alguém de aplicar isso a certos tipos primitivos e recuperar comportamentos inesperados (o que poderia ser uma coisa boa); mas vamos supor que eu abreviei o código acima e que você, de fato, possui sobrecargas para todos os tipos primitivos, bem como para Object
s. Então este próximo pedaço de código é realmente mais apropriado:
bool stringIsTrue(untyped arg)
{
if (arg.toString() == "0")
{
return false;
}
else
{
return true;
}
}
Mas e se você só precisava usar isso para int
s e string
s, e se você quer que ele retornar verdadeiro com base em condições mais simples ou mais complicado nesse sentido? Então você tem um bom motivo para usar a 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;
}
}
Mas ei, por que não dar a essas funções dois nomes diferentes? Você ainda tem a mesma quantidade de controle refinado, não é?
Porque, como afirmado anteriormente, alguns hotéis usam números, alguns usam letras e alguns usam uma mistura de números e 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 ainda não é exatamente o mesmo código exato que eu usaria na vida real, mas deve ilustrar o ponto que estou fazendo bem.
Mas ... Eis por que não é mais que açúcar sintático nas línguas contemporâneas.
Falco levantou a questão nos comentários de que os idiomas atuais basicamente não combinam sobrecarga de método e seleção dinâmica de função na mesma etapa. A maneira como eu entendi anteriormente que certas linguagens funcionavam era que você poderia sobrecarregar appearsToBeFirstFloor
no exemplo acima e, em seguida, a linguagem determinaria em tempo de execução qual versão da função seria chamada, dependendo do valor em tempo de execução da variável não digitada. Essa confusão resultou parcialmente do trabalho com tipos de linguagens ECMA, como o ActionScript 3.0, no qual você pode facilmente randomizar qual função é chamada em uma determinada linha de código em tempo de execução.
Como você deve saber, o ActionScript 3 não suporta sobrecarga de métodos. Quanto ao VB.NET, você pode declarar e definir variáveis sem atribuir um tipo explicitamente, mas ao tentar passar essas variáveis como argumentos para métodos sobrecarregados, ele ainda não deseja ler o valor do tempo de execução para determinar qual método chamar; em vez disso, deseja encontrar um método com argumentos do tipo Object
ou nenhum tipo ou algo assim. Portanto, o exemplo int
vs. string
acima também não funcionaria nesse idioma. O C ++ tem problemas semelhantes, pois quando você usa algo como um ponteiro nulo ou algum outro mecanismo como esse, ainda é necessário desambiguar manualmente o tipo em tempo de compilação.
Então, como o primeiro cabeçalho diz ...
Para idiomas contemporâneos, é apenas açúcar sintático; de uma maneira completamente independente da linguagem, é mais do que isso. Tornar a sobrecarga de método mais útil e relevante, como no exemplo acima, pode realmente ser um bom recurso para adicionar a um idioma existente (como tem sido amplamente implicitamente solicitado para o AS3), ou também pode servir como um entre muitos pilares fundamentais diferentes para a criação de uma nova linguagem processual / orientada a objetos.