So erzwingen Sie den Aufruf nur der Methoden der Oberklasse, obwohl diese überschrieben wurden (in Ocaml)

StackOverflow https://stackoverflow.com/questions/4829597

Frage

Ehrlich gesagt weiß ich nicht viel über das Objektsystem von OCaml.Der folgende Ausschnitt veranschaulicht mein Problem:

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

Bei der Ausführung erzeugt das Snippet die folgende Ausgabe:

bar2
foo1
bar2
foo1
bar2
endbar

, was zeigt, dass die Oberklassenmethode foo1 (aufgerufen über super#foo1) die überschriebene Methode foo2 aus der Unterklasse ausführt.Ich hätte stattdessen erwartet, dass es die Methode foo2 aus der Superklasse aufruft, wie sie über super aufgerufen wird.

Ist es möglich, dieses Verhalten zu erreichen, d.h.Soll eine Superklassenmethode nur andere Superklassenmethoden aufrufen, auch wenn diese in einer Unterklasse überschrieben werden?

War es hilfreich?

Lösung

Ich bin mir nicht 100% sicher, aber ich bin mir ziemlich sicher, dass Sie es nicht können. In OCAML sind Vererbung und Subtyping zwei verschiedene Konzepte. Eine Klasse kann ein Subtyp eines anderen Typs ohne Vererbung sein. Alles, was die Vererbung tut, ist, die Methoden aus der ererbten Klasse zu ziehen.

Der Polymorphismus wird durch strukturelle Typisierung erreicht, sodass Ihr Aufruf dazu foo2 Ruft die Methode in auf bar Weil bar hat implementiert foo2 und nicht weil bar Erben von foo (Wie angelegt, C ++, wo bar#foo2 wird aufgrund einer virtuellen Funktionstabelle aufgerufen).

Das heißt, OCAML bietet Ihnen eine Möglichkeit, zwischen überschriebenen Methoden und ererbten Methoden zu unterscheiden, die mit dem vererbt wurden inherit...as... Syntax. Im bar Aus Ihrem Beispiel, o#foo1 und super#foo1 sind die gleiche Methode (seitdem bar Implementiert nicht foo1) wohingegen o#foo2 und super#foo2 sind verschiedene Methoden. Trotzdem glaube ich nicht, dass die ererbte Klasse sowieso wissen, dass sie zwischen den Methoden und den überschriebenen Methoden geerbt und unterscheidet. Dafür mag es eine Syntax geben, aber ich bezweifle sehr, dass Vererbung und Polymorphismus unabhängige Konzepte sind.

Andere Tipps

Nein, ist es nicht. Dies ist "späte Bindung", eine grundlegende Idee von OO, nicht spezifisch für OCAML.

Ich weiß nicht, was Sie versuchen, aber objektorientierte Programmierung ist vielleicht nicht die richtigen Werkzeuge oder zumindest nicht so. Wenn Sie OCAML lernen möchten, sollten Sie sich wahrscheinlich auf die Nicht-OO-Teile konzentrieren, was für den Anfang interessant genug ist.

Wie Gasche betont, ist dies das beabsichtigte und Standardverhalten für OO -Sprachen.

Das super#foo1 Rufen Sie seit bar Überschreibt nicht foo1, ist genau gleichwertig zu o#foo1. Das super Konstrukt existiert nur, um das aufrufen zu können foo2 Methode von foo Aus Methoden in bar (Ansonsten gibt es keine Möglichkeit, diese Methode zu verweisen). Im foo1 wie genannt von bar#foo2, o ist eigentlich a bar, kein foo, so ruft das an foo2 Methode ruft die auf bar foo2 Methode.

Ich würde sagen, wenn Sie dieses Verhalten fest codieren möchten, sollten Sie besser keine objektorientierte Programmierung verwenden.Implementieren Sie es einfach als Funktionen, die andere Funktionen aufrufen.

(„dieses Verhalten“, wie ich es verstanden habe:wenn Sie anrufen foo2 aus dem Code heraus, der aufgerufen wurde als super#foo1, dann genau das foo2 aus der Implementierung der Oberklasse sollte aufgerufen werden, nicht aus den „spezifischeren“ Implementierungen von Unterklassen)

Es ist die einfachste, sauberste und klarste Vorgehensweise:Programmfunktionen, die das tun, was Sie wollen.

Oder Sie sollten es sich und uns erklären:Warum brauchen Sie OOP?Der Grund dafür wird im Fragetext nicht genannt.Warum machen foo1 Und foo2 Methoden statt unabhängiger Funktionen?(Abgesehen von foo1 Und foo2, Möglicherweise enthält Ihr Programm einige Objekte, Klassen und Methoden (sofern zutreffend).

Ich frage mich, ob diese Frage aus dem Vergleich mit anderen Sprachen resultiert

Wenn Sie eine andere OO-Sprache beherrschen, ist es seltsam, dass Sie „dieses Verhalten“ von OOP erwarten:Es ist nicht das Verhalten, das beispielsweise in Java oder C++ erwartet wird, da sie das Konzept einer Tabelle virtueller Methoden verwenden, die jedem Objekt zur Laufzeit zugeordnet wird. Wenn Sie also eine Methode in Ihrem Programm aufrufen, wird sie zur Laufzeit abgesetzt. Zeit bis zur Implementierung der tatsächlich mit dem Objekt verknüpften Methode.Kurz gesagt: Wann immer Sie in Ihrem Programm einen Methodenaufrufausdruck verwenden, verpflichten Sie sich zu diesem Prinzip, die Implementierung der Methode zu finden („späte Bindung“)., wie Gasche betonte.Obwohl immer noch vgl.die Unterschiede zwischen OCaml und mit einer virtuellen Methodentabelle implementierten Sprachen, auf die Niki Yoshiuchi hingewiesen hat.

Formalisierung der gesamten Diskussion über die verfügbaren und gewünschten Verhaltensweisen

Obwohl das gewünschte Verhalten möglicherweise nicht das erwartete und verfügbare Verhalten in vielen gängigen OO-Sprachen ist, ist es vorstellbar und könnte in einigen spezifischen OOP-Systemen umsetzbar sein, wenn man Zugriff auf die Interna der OOP-Implementierung hat.

Sagen wir, wenn in einer Implementierung, super ist eine Struktur, die die Methodentabelle der Oberklasse enthält (auf die beim Auflösen eines Methodenaufrufs für das aktuelle Objekt zurückgegriffen werden kann), und die Methoden sind Funktionen, die das Objekt (die Methodentabelle) als erstes Argument empfangen müssen, um dann das auszuführen, was Sie tun wollen, würde man schreiben super.foo1(super, y).

(Ich frage mich tatsächlich, ob es OOP-Implementierungen gibt, deren Interna dem Programmierer offengelegt werden und die einen solchen Aufruf ermöglichen.)

Während das übliche OOP-Verhalten in diesem System durch ausgedrückt würde this.foo1(this, y) (Wo this ist die Methodentabelle für das aktuelle Objekt.)

Ihr OCaml-Anruf super#foo1 y oder ein Java super.foo1(y); übersetzt in dieses „mein“ System als super.foo1(this, y).(Obwohl noch vgl.die von Niki Yoshiuchi aufgezeigten Unterschiede zwischen OCaml und Sprachen wie Java, die mit einer virtuellen Methodentabelle implementiert werden.)

Sie sehen die Unterschiede zwischen den 3 Fällen.

Anhang.Auf der Suche nach Sprachen, die auf diese Weise funktionieren würden

Hmm, PHP könnte eine Sprache sein, in der dieses Verhalten möglich wäre, aber:

  • nur bei der Programmierung auf „Klassenebene“ (statische Methoden), nicht auf Objektebene;
  • Nur wenn Sie etwas Seltsames fest codieren „späte statische Bindung“ bei den Funktionsaufrufen:

#!/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);
?>

Das Modell verhält sich gewissermaßen wie Standard-OO-Objekte mit virtuellen Methoden und Bar as du wolltest:

$ ./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
$ 

(Übrigens, mit parent:: anstatt Foo:: würde uns nicht zu Ihrem gewünschten Verhalten bringen.)

Ich verstehe den Zweck der verrückten PHP-Bindungsspezifikationen nicht static::, die nur für statische Methoden (d. h. Programmierung auf Klassenebene) von gewisser Wirkung sind.

Ein analoges C++-Beispiel gibt standardmäßig kein OO-Verhalten auf Objektebene an:


#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;
}

-- es gibt Ihr gewünschtes Verhalten:

$ ./a.out | head -10
bar2
foo1
foo2
foo2
foo2
foo2
foo2
foo2
foo2
foo2
$ 

auch ohne spezielles Qualifikationsmerkmal beim Funktionsaufruf im Code für Bar::foo2(), also für uns nicht interessant.

Was ist mit den statischen Methoden von Java?(Unterscheiden sie sich von C++ und bieten sie uns standardmäßig eine virtuelle, methodenähnliche Auflösung der Funktionsaufrufe?)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top