Perché “evitare il sovraccarico del metodo”?

Perché Jorge Ortiz consiglia di evitare il sovraccarico del metodo?

Il sovraccarico rende un po ‘più difficile sollevare un metodo per una funzione:

object A { def foo(a: Int) = 0 def foo(b: Boolean) = 0 def foo(a: Int, b: Int) = 0 val function = foo _ // fails, must use = foo(_, _) or (a: Int) => foo(a) } 

Non è ansible importare selettivamente uno di un set di metodi sovraccaricati.

C’è una maggiore possibilità che si verifichi un’ambiguità quando si tenta di applicare viste implicite per adattare gli argomenti ai tipi di parametri:

 scala> implicit def S2B(s: String) = !s.isEmpty S2B: (s: String)Boolean scala> implicit def S2I(s: String) = s.length S2I: (s: String)Int scala> object test { def foo(a: Int) = 0; def foo(b: Boolean) = 1; foo("") } :15: error: ambiguous reference to overloaded definition, both method foo in object test of type (b: Boolean)Int and method foo in object test of type (a: Int)Int match argument types (java.lang.String) object test { def foo(a: Int) = 0; def foo(b: Boolean) = 1; foo("") } 

Può tranquillamente rendere inutilizzabili i parametri predefiniti:

 object test { def foo(a: Int) = 0; def foo(a: Int, b: Int = 0) = 1 } 

Individualmente, questi motivi non ti obbligano a evitare completamente il sovraccarico. Mi sento come se mi mancassero alcuni problemi più grandi.

AGGIORNARE

Le prove si stanno accumulando.

  • Complicano le specifiche
  • Può rendere gli insiti inadatti all’uso in vista.
  • Limita l’introduzione di valori predefiniti per i parametri solo su una delle alternative sovraccaricate.
  • Poiché gli argomenti verranno digitati senza un tipo previsto, non è ansible passare letterali di funzioni anonimi come “_.foo” come argomenti a metodi sovraccaricati .

AGGIORNAMENTO 2

  • Non è ansible (al momento) utilizzare metodi sovraccaricati in oggetti pacchetto.
  • Gli errori di applicabilità sono più difficili da diagnosticare per i chiamanti della tua API.

AGGIORNAMENTO 3

  • la risoluzione di sovraccarico statico può rubare un’API di tutti i tipi di sicurezza:
 scala> object O { def apply[T](ts: T*) = (); def apply(f: (String => Int)) = () } defined object O scala> O((i: String) => f(i)) // oops, I meant to call the second overload but someone changed the return type of `f` when I wasn't looking... 

Le ragioni che danno Gilad e Jason (retronym) sono tutte ottime ragioni per evitare il sovraccarico, se ansible. Le ragioni di Gilad si concentrano sul perché il sovraccarico è problematico in generale, mentre le ragioni di Jason si concentrano sul perché è problematico nel contesto di altre funzionalità di Scala.

Alla lista di Jason, vorrei aggiungere che l’overloading interagisce male con l’inferenza di tipo. Prendere in considerazione:

 val x = ... foo(x) 

Una modifica nel tipo inferito di x potrebbe alterare quale metodo foo viene chiamato. Il valore di x non deve cambiare, solo il tipo di x inferito, che potrebbe accadere per tutti i tipi di ragioni.

Per tutti i motivi addotti (e alcuni altri sono sicuro che mi sto dimenticando), penso che il sovraccarico di metodo dovrebbe essere usato con la massima parsimonia ansible.

Penso che il consiglio non sia inteso per scala in particolare, ma per OO in generale (finora so che scala dovrebbe essere un best-of-breed tra OO e funzionale).

L’override va bene, è il cuore del polimorfismo ed è centrale nel design OO.

Sovraccarico d’altra parte è più problematico. Con il sovraccarico del metodo è difficile distinguere quale metodo verrà realmente invocato ed è spesso una fonte di confusione. Raramente c’è anche una giustificazione per cui il sovraccarico è davvero necessario. Il problema può essere risolto per la maggior parte del tempo in un altro modo e concordo sul fatto che il sovraccarico è un odore.

Ecco un articolo che spiega bene cosa intendo con “sovraccaricare è fonte di confusione”, che ritengo sia il motivo principale per cui è scoraggiato. È per java, ma penso che valga anche per scala.