0

I have to call an instance variable initialized in a parent Class from a sub class. I know that I can solve using "@@profile" instead of "@profile" but it needs to be thread-safe.

class ParentClass

    attr_reader :profile

    def initialize(profile:)
        @profile = profile
    end

    def call_sub
        SubClass.new().run()
    end

end

class SubClass < ParentClass

    def initialize()
        @retries = 0
    end

    def run()
        puts "PROFILE: #{@profile.inspect}"
    end

end

pc = ParentClass.new(profile: "Me")
pc.call_sub

p = ParentClass.new(profile: "Me")
puts "PROFILE: #{p.profile}"

ruby test.rb

PROFILE: nil <--- Why is nil ?
PROFILE: Me
2
  • 1
    Because SubClass overwrites initialize, takes no arguments, and does not call super. Change the SubClass#initialize to def initialize(profile:); super; @retries= 0;end and change ParentClass#call_sub to SubClass.new(profile: self.profile).run another alternative (probably better) is remove initialize from SubClass and define def retries; @retries ||= 0; end ( you will still have to pass the profile kwarg to new in call_sub then everything will work as expected. Commented Jan 23, 2022 at 18:57
  • 1
    In addition to what engineerminsky said about your initializer, you can't treat a mutable shared variable or object as thread-safe. You probably need to use Thread-local variables or use mutexes on access to an injected Profile object. That probably means rethinking your architecture, or reframing what you're really trying to do with the Profile object across threads. Commented Jan 23, 2022 at 20:53

1 Answer 1

1

You are overriding ParentClass#initialize.

Ruby does not magically invoke overridden methods. That would kind of defeat the point of overriding in the first place: for example, often, the reason why you override a method in a subclass, is that the subclass implementation can make use of its knowledge of the subclass to improve performance. It wouldn't make sense for Ruby to invoke the un-optimized version in addition to the optimized one.

However, another big purpose of overriding is differential code reuse: reusing code by only implementing the difference in behavior. In order to do that, Ruby has the super keyword, which allows you to retry the method lookup starting one level up in the ancestors chain.

Here's what that would look like in your case:

class SubClass < ParentClass
  def initialize(*)
    super
    @retries = 0
  end
end

This solves the problem with the un-initialized instance variable.

However, there are several other problems with your code. For example, here:

SubClass.new().run()

You are sending the message new to SubClass without any arguments. By default, Class#new is implemented roughly like this:

class Class
  def new(...)
    obj = allocate
    obj.initialize(...)
    obj
  end
end

[Technically, initialize is private, so we need to break encapsulation here by using something more like obj.__send__(:initialize, ...).]

In other words, new passes its arguments on to initialize. But you have defined ParentClass#initialize with one mandatory keyword parameter profile:, so theoretically, we need to pass an argument to super … but what should we pass? So, instead, we have defined SubClass#initialize to take arguments and pass them on to ParentClass#initialize.

However, you are passing no arguments to SubClass::new here, that we could pass on, and in fact, it isn't clear what arguments we could pass here.

I would like to help you here, but to be honest, the code simply does not make sense, so I don't see how to fix it. Superclasses must never know about their subclasses, in fact, they typically cannot know about their subclasses: many languages allow you to add subclasses after the fact, and Ruby is no exception. Therefore, a superclass knowing about its subclass is a sign that something in your design is very, very, very wrong.

Also note that the very idea of a "parent instance variable" does not make sense: "parent" refers to the class hierarchy, but instance variables belong to instances … that's why they are called "instance" variables, after all.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.