2017-01-03 3 views
3

Ich dachte, dass ein Modul als Mixin in einer Klasse "hinzugefügt die Funktionen" der Klasse hinzugefügt.Aufruf "Super" Schlüsselwort mit Modulen und Vererbung

Ich verstehe nicht, warum das nicht so funktioniert erwartet:

module A 
    def blah 
     super if defined?(super) 
     puts "hello, world!" 
    end 
end 

class X 
    include A 
end 

class Y < X 
    include A 
end 

y = Y.new 
y.blah 

Ich hatte erwartet, „y“ seine super blah() aufzurufen (seit seiner in der Klasse X enthalten?), Sondern ich habe:

test.rb: 3: in blah: super: kein Super Methode `blah‘

Antwort

7

Sie sind in Nuancen von Rubys Objekthierarchie ausgeführt wird und wie Methode Lookups mit inc interagieren bezahlte Module.

Wenn Sie eine Methode für ein Objekt, Ruby walks over the ancestors list für die Klasse des Objekts aufrufen, suchen Sie nach einer Vorgängerklasse oder einem Modul, das auf diese Methode reagiert. Wenn Sie super in dieser Methode aufrufen, sind Sie effektiv fortfahren Ihr zu Fuß den Baum von ancestors, auf der Suche nach dem nächsten Objekt, das auf den gleichen Methodennamen reagiert.

Der Vorfahr Baum für Ihre X und Y Klassen wie folgt aussehen:

p X.ancestors #=> [ X, A, Object, Kernel, BaseObject ] 
p Y.ancestors #=> [ Y, X, A, Object, Kernel, BaseObject ] 

Das Problem ist, dass include das Modul ein zweites Mal ing, in einer untergeordneten Klasse, hat nicht eine zweite Kopie von injizieren das Modul in der Ahnenkette.

Effektiv was passiert, wenn Sie Y.new.blah aufrufen, beginnt Ruby nach einer Klasse zu suchen, die auf blah reagiert. Es geht an Y und X vorbei und landet auf A, das die blah-Methode einführt. Wenn A#blahsuper aufruft, zeigt der "Zeiger" in Ihrer Vorfahrenliste bereits auf A, und Ruby sucht weiter nach einem anderen Objekt, das auf Object, Kernel reagiert, und dann BaseObject. Keine dieser Klassen verfügt über eine blah-Methode, sodass Ihr Aufruf super fehlschlägt.

Eine ähnliche Sache passiert, wenn ein Modul A ein Modul B enthält und dann eine Klasse beide Module A und B enthält. Das B Modul ist nicht zweimal enthalten:

module A; end 
module B; include A; end 

class C 
    include A 
    include B 
end 

p C.ancestors # [ C, B, A, Object, Kernel, BaseObject ] 

Beachten Sie, dass es C, B, A, nicht C, A, B, A.

Die Absicht scheint zu sein, damit Sie sicher super innerhalb von A 's Methoden aufrufen können, ohne sich Gedanken darüber zu machen, wie der Verbrauch von Klassenhierarchien versehentlich A zweimal enthalten könnte.


Es gibt ein paar Experimente, die verschiedene Aspekte dieses Verhaltens demonstrieren.Das erste ist das Hinzufügen eines blah Verfahren, um Objekt, das die super Aufruf passieren lässt:

class Object; def blah; puts "Object::blah"; end; end 

module A 
    def blah 
    puts "A::blah" 
    super 
    end 
end 

class X 
    include A 
end 

class Y < X 
    include A 
end 

Y.new.blah 

# Output 
# A::blah 
# Object::blah 

Der zweite Versuch ist, zwei Module zu verwenden, BaseA und A, welche tut Ursache die Module zweimal eingeführt werden, richtig, in der ancestors Kette:

module BaseA 
    def blah 
    puts "BaseA::blah" 
    end 
end 

module A 
    def blah 
    puts "A::blah" 
    super 
    end 
end 

class X 
    include BaseA 
end 

class Y < X 
    include A 
end 

p Y.ancestors # [ Y, A, X, BaseA, Object, ...] 
Y.new.blah 

# Output 
# A::blah 
# BaseA::blah 

Ein dritte experiement prepend statt include verwendet, die das Modul vor platziert des Objekts in der ancestors Hierarchie und interessanterweise does eine Kopie des Moduls einfügen. Dies ermöglicht es uns, den Punkt zu erreichen, an dem effektiv Y::blahX::blah aufruft, die fehlschlägt, weil Object::blah existiert nicht:

require 'pry' 

module A 
    def blah 
    puts "A::blah" 
    begin 
     super 
    rescue 
     puts "no super" 
    end 
    end 
end 

class X 
    prepend A 
end 

class Y < X 
    prepend A 
end 

p Y.ancestors # [ A, Y, A, X, Object, ... ] 
Y.new.blah 

# Output 
# A::blah (from the A before Y) 
# A::blah (from the A before X) 
# no super (from the rescue clause in A::blah) 
+0

Sehr interessant und lehrreich. Was meinst du mit "Ich kann nicht vollständig erklären" am Anfang deines Eröffnungssatzes. –

+0

@CarySwoveland Ich meinte, dass diese Antwort auf Hypothesen und Experimenten beruht, die diese Hypothese zu stützen scheinen, und nicht auf einer verbindlichen Erklärung und einem Link zu unterstützender Dokumentation. – meagar

+0

@CarySwoveland Jedenfalls habe ich die Formulierung etwas aktualisiert. – meagar

Verwandte Themen