Come forzare chiamare solo metodi della superclasse, nonostante che sia stato uno scostamento (in OCaml)
-
26-10-2019 - |
Domanda
Per essere onesti, non so molto circa il sistema oggetto di OCaml. Il seguente frammento di codice illustra il mio problema:
class foo =
object (o)
method foo1 y =
print_endline "foo1";
o#foo2 (y - 1)
method foo2 y =
print_endline "foo2";
if y > 0 then o#foo2 y else print_endline "end"
end
class bar =
object (o : 'self_type)
inherit foo as super
method foo2 y =
print_endline "bar2";
if y > 0 then super#foo1 y else print_endline "endbar"
end
let _ = (new bar)#foo2 3
Quando eseguito, il frammento produce il seguente output:
bar2
foo1
bar2
foo1
bar2
endbar
, dimostrando che il metodo della superclasse foo1 (chiamata via eccellente # foo1) esegue il metodo foo2 sovresposta dalla sottoclasse. Vorrei invece aspettato a chiamare il metodo foo2 dalla superclasse, come viene chiamato tramite eccellente.
E 'possibile ottenere questo comportamento, vale a dire avere una chiamata di metodo della superclasse soli altri metodi della superclasse anche se sono sovrascritti in una sottoclasse?
Soluzione
Non sono sicuro al 100% su questo, ma sono abbastanza sicuro che non si può. In OCaml eredità e subtyping sono due concetti diversi. Una classe può essere un sottotipo di un altro tipo, senza eredità. Tutto eredità fa è tirare nei metodi della classe ereditata.
polimorfismo avviene tramite digitazione strutturale, quindi la chiamata al foo2
chiama il metodo in bar
perché bar
ha implementato foo2
e non perché eredita bar
da foo
(come apposto a dire, C ++, dove bar#foo2
è chiamato a causa di una tabella di funzione virtuale guardare in alto).
Detto questo, OCaml non vi fornirà un modo per distinguere tra i metodi sostituiti e metodi ereditati utilizzando la sintassi inherit...as...
. In bar
dal vostro esempio, o#foo1
e super#foo1
sono lo stesso metodo (poiché bar
non implementa foo1
) che o#foo2
e super#foo2
sono metodi differenti. Nonostante questo, non credo che ci sia in ogni caso per la classe ereditata per sapere che è stato ereditato e distinguere tra IT di metodi ed i metodi sostituiti. Ci potrebbe essere qualche sintassi per questo, ma dubito fortemente che a causa del fatto che ereditarietà e polimorfismo sono concetti indipendenti.
Altri suggerimenti
No non lo è. Questo è "tardiva", un'idea di base di OO, non specifiche per OCaml.
Non so quello che stai cercando di fare, ma la programmazione orientata agli oggetti, non è forse gli strumenti giusti, o almeno non utilizzato in questo modo. Se volete imparare OCaml, probabilmente si dovrebbe concentrarsi sulle parti non OO, che è abbastanza interessante per cominciare.
Come sottolinea Gasche, questo è il comportamento previsto e standard per i linguaggi OO.
La chiamata super#foo1
, dal momento che bar
non sovrascrive foo1
, è esattamente equivalente a o#foo1
. Il costrutto super
esiste solo essere in grado di chiamare il metodo foo2
di foo
metodi in bar
(altrimenti non c'è modo fare riferimento a tale metodo). In foo1
come richiesto dal bar#foo2
, o
è in realtà un bar
, non un foo
, quindi richiamare il metodo foo2
invoca il metodo bar
foo2
.
Direi che se si vuole hardcode che il comportamento, è meglio non utilizzare la programmazione orientata agli oggetti. Semplicemente attuarla come funzioni chiamare altre funzioni.
( "quel comportamento", come intesa da me: se chiamate foo2
dall'interno del codice che è stato chiamato come super#foo1
, quindi esattamente la foo2
dall'attuazione della superclasse dovrebbe essere chiamato, non dai più "specifici" implementazioni da sottoclassi)
E 'il modo più semplice e più pulito e più chiaro per procedere:. Funzioni del programma che fare quello che vuoi
In alternativa si dovrebbe spiegare a se stessi ea noi: Perché avete bisogno OOP? La ragione per questo non è dato nel testo della domanda. Perché fare foo1
e metodi foo2
piuttosto che funzioni indipendenti? (A parte foo1
e foo2
, è possibile che alcuni oggetti e classi e metodi nel vostro programma, se del caso).
Chiedendosi se questa domanda viene da confronto ad altri lnaguages ??
Se si conosce altro linguaggio OO, che è strano che si desidera "che il comportamento" da OOP: non è il comportamento previsto, per esempio, Java o C ++, perché impiegano il concetto di una tabella di metodi virtuali che è associato con ogni oggetto a run-time, in modo da quando si chiama un metodo nel vostro programma, esso viene inviato in fase di esecuzione per l'attuazione di tale metodo in realtà associato con l'oggetto. Così, in breve: ogni volta che si utilizza l'espressione metodo-chiamata nel programma, ti impegni a questo principio di trovare l'implementazione del metodo ( "associazione tardiva") , come fuori puntato da Gasche. Anche se ancora cf. le differenze tra OCaml e lingue implementate con un tavolo metodi virtuali sottolineato da Niki Yoshiuchi.
formalizzazione di tutti la discussione dei comportamenti disponibili e ricercati
Anche se ciò che si desidera potrebbe non essere il comportamento previsto e disponibile in molti linguaggi OO popolari, è immaginabile e potrebbe essere implementabile in alcuni sistemi specifici OOP, se si ha accesso alle parti interne di implementazione OOP.
Say, se in qualche realizzazione, super
è una struttura che tiene la tavola metodi della superclasse (di fallback a quando si risolve un invito metodo per l'oggetto corrente), ed i metodi sono funzioni che devono ricevere l'oggetto (tabella metodi ) come il primo arg, quindi di eseguire ciò che si vuole, si potrebbe scrivere super.foo1(super, y)
.
(io in realtà chiedo se ci sono implementazioni OOP i cui interni sono esposti al programmatore e che consentono di fare una tale chiamata.)
considerando normale comportamento UPU sarebbe espressa in questo sistema this.foo1(this, y)
(dove this
è la tabella metodi per l'oggetto corrente.)
La chiamata super#foo1 y
OCaml o un super.foo1(y);
Java si traduce in questo "mio" sistema super.foo1(this, y)
. (Anche se ancora cfr le differenze evidenziate da Niki Yoshiuchi tra OCaml e linguaggi come Java realizzato con un tavolo metodi virtuali.)
Si vede le differenze tra i 3 casi.
Appendice. Guardando per le lingue che funziona in questo modo
Hmm, PHP potrebbe essere un linguaggio in cui tale comportamento sarebbe stato possibile, ma:
- solo sul "livello di classe" programmazione (metodi statici), non oggetto di livello;
- solo quando si hardcode qualche strana "binding statico in ritardo " alle chiamate di funzione:
#!/usr/bin/php
<?php
class Foo {
public static function foo1($y) {
echo "foo1\n";
static::foo2($y-1);
}
public static function foo2($y) {
echo "foo2\n";
if ($y > 0)
static::foo2($y);
else
echo "end\n";
}
}
class Bar extends Foo {
public static function foo2($y) {
echo "bar2\n";
if ($y > 0)
Foo::foo1($y);
else
echo "endbar\n";
}
}
class Model extends Foo {
public static function foo2($y) {
echo "model2\n";
if ($y > 0)
static::foo1($y);
else
echo "endmodel\n";
}
}
Model::foo2(3);
Bar::foo2(3);
?>
I si comporta modello in un certo senso come OO standard di oggetti con metodi virtuali, e bar come si voleva :
$ ./test-force-super-binding.php | head -20 model2 foo1 model2 foo1 model2 foo1 model2 endmodel bar2 foo1 foo2 foo2 foo2 foo2 foo2 foo2 foo2 foo2 foo2 foo2 $
(BTW, utilizzando parent::
invece di Foo::
non otterrebbe noi al vostro comportamento voluto.)
Non capisco il prupose delle specifiche vincolanti del PHP folle come static::
, che sono di qualche effetto solo per i metodi statici (cioè, programmazione classe-livello).
Un C ++ analoga aperta esempio non dà un comportamento livello oggetto OO di default:
#include<iostream>
class Foo {
protected:
static void foo1(int y) {
std::cout << "foo1" << std::endl;
foo2(y-1);
}
public:
static void foo2(int y) {
std::cout << "foo2" << std::endl;
if (y > 0)
foo2(y);
else
std::cout << "end" << std::endl;
}
};
class Bar: public Foo {
public:
static void foo2(int y) {
std::cout << "bar2" << std::endl;
if (y > 0)
foo1(y);
else
std::cout << "endbar" << std::endl;
}
};
int main() {
Bar::foo2(3);
return 0;
}
- si dà il vostro comportamento desiderato:
$ ./a.out | head -10 bar2 foo1 foo2 foo2 foo2 foo2 foo2 foo2 foo2 foo2 $
anche senza alcuna qualificazione speciale alla chiamata di funzione nel codice per Bar::foo2()
, quindi non interessante per noi.
Che dire di metodi statici di Java? .. (cosa differiscono da C ++ e ci danno un virtual-metodo-come la risoluzione della funzione-chiamate di default?)