Ruby中使用attr_accessor定义的实例变量外部访问为nil且报未定义方法错误
问题根因与面向对象封装逻辑解析
直接触发报错的表层原因
NoMethodError: undefined method 'has_key?' for nil:NilClass 本质是执行cur.children.has_key? char时,cur.children的返回值为nil,而nil本身没有has_key?方法,因此抛出错误。
深层根因拆解
1. 初始化方法拼写错误,实例变量从未完成赋值
Ruby中类的实例初始化钩子方法是固定拼写的initialize,类方法new的执行逻辑是:先为新实例分配内存空间,随后自动调用该实例的initialize方法完成初始化。
你在TrieNode中写的方法名是initalize(漏写了一个字母i),这个方法只会作为普通的自定义实例方法存在,完全不会在执行TrieNode.new时被自动调用。
这就导致你写在该方法里的@children = Hash.new这类赋值逻辑全程没有执行,所有TrieNode实例的@children、@value、@next实例变量从未被显式赋值。Ruby语言设计中,读取未显式赋值的实例变量会直接返回nil,不会抛出未定义实例变量的错误,只有当你对这个nil调用不存在的方法时,才会触发NoMethodError。
2. 对attr系列宏的封装逻辑存在误解
你使用attr_reader、attr_accessor的初衷是封装实例变量的访问权限,但这类宏的作用仅为自动生成对外的访问接口方法,并不会帮你初始化实例变量:
attr_reader :children等价于手写def children; @children; end,仅暴露读取@children的接口attr_accessor :root等价于同时生成def root; @root; end和def root=(val); @root = val; end读写两个接口- 无论使用哪种attr宏,都需要你在实例生命周期的合适节点(通常是
initialize方法)给对应的实例变量赋值,否则接口返回的结果永远是nil。
修正后的可运行代码
class TrieNode attr_reader :value, :next, :children # 修正方法拼写为标准初始化方法名 def initialize @value = nil @children = Hash.new @next = nil end end class Trie attr_accessor :root def initialize @root = TrieNode.new end def build_trie(strs) cur = @root strs.each do |str| str.chars.each do |char| if cur.children.has_key?(char) cur = cur.children[char] next else new_node = TrieNode.new cur.children[char] = new_node cur = new_node end end cur = @root end end end
内容的提问来源于stack exchange,提问作者Lollypop
相关产品推荐
相关产品推荐

