Для сучасних мов це просто синтаксичний цукор; цілком мовно-агностичним чином, це більше.
Раніше в цій відповіді було сказано просто, що це більше, ніж синтаксичний цукор, але якщо ви побачите в коментарях, Фалько підкреслив, що в головоломці є один фрагмент, про який, здається, у всіх сучасних мов не вистачає; вони не змішують перевантаження методу з динамічним визначенням, яку функцію викликати в одному кроці. Це буде з’ясовано пізніше.
Ось чому це повинно бути більше.
Розглянемо мову, яка підтримує як перевантаження методу, так і нетипізовані змінні. Ви можете мати такі прототипи методу:
bool someFunction(int arg);
bool someFunction(string arg);
У деяких мовах ви, мабуть, змирилися б з тим, що в момент компіляції дізнаєтесь, яка з них буде викликана заданим рядком коду. Але в деяких мовах не всі змінні вводяться (або всі вони неявно вводяться як- Object
небудь чи будь-які інші), тож уявіть собі створення словника, клавіші якого відображають значення різних типів:
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.
Тепер, що робити, якщо ви хочете звернутися someFunction
до одного з цих номерів? Ви називаєте це:
someFunction(roomNumber[someSortOfKey]);
Викликається someFunction(int)
, або someFunction(string)
називається? Тут ви бачите один приклад, коли це не зовсім ортогональні методи, особливо в мовах вищого рівня. Мова повинна визначити - під час виконання - яку з них викликати, тому вона все ще повинна вважати, що це хоча б дещо той самий метод.
Чому б просто не використовувати шаблони? Чому б просто не використовувати нетипізований аргумент?
Гнучкість і тонкий контроль. Іноді використання шаблонів / нетипізованих аргументів є кращим підходом, але іноді - ні.
Ви повинні думати про випадки, коли, наприклад, у вас можуть бути два підписи методу, які кожен бере аргумент int
a і a string
, але де порядок у кожного підпису різний. Можливо, у вас є вагомі підстави зробити це, оскільки виконання кожного підпису може зробити багато в чому одне і те ж, але з дещо іншим поворотом; наприклад, лісозаготівля може бути різною. Або навіть якщо вони будуть робити те саме, ви, можливо, зможете автоматично отримати певну інформацію лише з того порядку, в якому аргументи були вказані. Технічно ви можете просто використовувати оператори псевдопереключення, щоб визначити тип кожного з переданих аргументів, але це стає безладним.
Так це наступний приклад поганої практики програмування?
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;
}
}
Так, за великим рахунком. У цьому конкретному прикладі це може перешкоджати комусь намагатися застосувати це до певних примітивних типів та повернути несподівану поведінку (що може бути хорошою справою); але давайте припустимо, що я скоротив код вище, і що ви, насправді, маєте перевантаження для всіх примітивних типів, а також для Object
s. Тоді цей наступний біт коду справді доречніше:
bool stringIsTrue(untyped arg)
{
if (arg.toString() == "0")
{
return false;
}
else
{
return true;
}
}
Але що робити, якщо вам потрібно було використовувати це лише для int
s і string
s, і що, якщо ви хочете, щоб він повернув справжнє на основі більш простих або складних умов відповідно? Тоді у вас є вагомий привід використовувати перевантаження:
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;
}
}
Але ей, чому б просто не дати цим функціям дві різні назви? Ви все ще маєте таку ж кількість дрібнозернистого контролю, чи не так?
Тому що, як було сказано раніше, деякі готелі використовують цифри, деякі використовують літери, а деякі використовують суміш цифр і букв:
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
Це все ще не той самий точний код, який я використовував би в реальному житті, але він повинен ілюструвати точку, яку я вказую просто чудово.
Але ... Ось чому це не більше ніж синтаксичний цукор у сучасних мовах.
У коментарях Falco підкреслив, що поточні мови в основному не поєднують перевантаження методу та динамічний вибір функцій на одному кроці. Те, як я раніше розумів деякі мови для роботи, - це те, що ви можете перевантажуватисьappearsToBeFirstFloor
у наведеному вище прикладі, і тоді мова визначатиме під час виконання, яку версію функції потрібно викликати, залежно від значення часу виконання нетипізованої змінної. Ця плутанина частково випливала з роботи з мовами ECMA, такими як ActionScript 3.0, в якій ви можете легко рандомізувати, яка функція отримує виклик у певному рядку коду під час виконання.
Як ви можете знати, ActionScript 3 не підтримує метод перевантаження. Що стосується VB.NET, ви можете оголошувати та встановлювати змінні, не призначаючи тип явно, але коли ви намагаєтесь передати ці змінні як аргументи перевантаженим методам, він все ще не хоче читати значення часу виконання, щоб визначити, який метод викликати; він натомість хоче знайти метод з аргументами типу Object
або відсутністю типу чи чимось подібним. Отже, int
порівняно з string
наведеним вище прикладом також не працюватиме на цій мові. У C ++ є подібні проблеми, тому що коли ви використовуєте щось на зразок пустотілого вказівника чи якийсь інший подібний механізм, він все одно вимагає від вас вручну відключити тип під час компіляції.
Отже, як говорить перший заголовок ...
Для сучасних мов це просто синтаксичний цукор; цілком мовно-агностичним чином, це більше. Зробити метод перевантаження більш корисним та релевантним, як у наведеному вище прикладі, насправді може бути корисною функцією для додавання до існуючої мови (як це поширювалося неявно для AS3), а також може слугувати одним із безлічі різних фундаментальних стовпів створення нової процедурної / об’єктно-орієнтованої мови.