Are Ruby class variables similar to the Java static variables?

时间:2016-08-31 17:24:26

标签: java ruby

I am transitioning to Ruby from Java and trying to understand Ruby scoping.

It seems class variables behave the same as static variables in Java. Is this interpretation correct? If so, are Ruby global variables then equivalent to public static variables in Java?

3 个答案:

答案 0 :(得分:2)

I'm not familiar with java, but I can explain in Ruby terms.

In Ruby, class variables have @@ before the variable name. It doesn't behave as you think it should. Once a class variable is created, it is shared among all the subclasses. It is not limited to the class you create it in.

As for global variables, it has $ before the variable name. It does exactly what it sounds like it does, it is global and can be access anywhere.

Both of the variables are not recommended in Ruby. You can do what you need using instance variables, which has @ before the name. It is specific to the object. If you declare it in one object, it will not show up in another.

I hope this clears this up.

答案 1 :(得分:2)

There's a lot of similarity between Ruby and Java by virtue of them being object-oriented, but their family tree is different. Ruby leans very heavily on Smalltalk while Java inherits from the C++ school of thinking.

The difference here is that Ruby's concept of public/private/protected is a lot weaker, they're more suggestions than rules, and things like static methods or constants are more of a pattern than a construct in the language.

Global variables are frowned on quite heavily, they can cause chaos if used liberally. The Ruby way is to namespace things:

$ugly_global = 0  # Not recommended, could conflict with other code
                  # Ownership of this variable isn't made clear.

$ugly_global += 1 # Works, but again, it's without context.

module UglyCounter    # Defines a module/namespace to live in
  def self.current    # Defines a clear interface to this value
    @counter ||= 0    # Initializes a local instance variable
  end

  def self.current=(v)   # Allow modification of this value
    @counter = v.to_i    # A chance to perform any casting/cleaning
  end
end

UglyCounter.current += 1   # Modifies the state of a variable, but
                           # the context is made clear.

Even a thin layer like this module gives you the ability to intercept read/write operations from this variable and alter the behaviour. Maybe you want to default to a particular value or convert values into a normalized form. With a bare global you have to repeat this code everywhere. Here you can consolidate it.

Class variables are a whole different thing. They're also best avoided because sharing data between the class and instances of this class can be messy. They're two different contexts and that separation should be respected.

class MessyClass
  @@shared = 0

  def counter
    @@shared
  end

  def counter=(v)
    @@shared = v
  end
end

This is a pretty rough take on how to use a shared class-level instance variable. The problem here is each instance is directly modifying it, bypassing the class context, which means the class is helpless. This is fundamentally rude, the instance is over-extending its authority. A better approach is this:

class CleanerClass
  def self.counter
    @counter ||= 0
  end

  def self.counter=(v)
    @counter = v.to_i
  end

  # These are reduced to simple bridge methods, nothing more. Because
  # they simply forward calls there's no breach of authority.
  def counter
    self.class.counter
  end

  def counter=(v)
    self.class.counter = v
  end
end

In many languages a static class method becomes available in the scope of an instance automatically, but this is not the case in Ruby. You must write bridge/proxy/delegate methods, the terminology here varying depending on what you're used to.

答案 2 :(得分:2)

一个重要的区别是类方法和实例变量在Ruby中以静态方法和实例变量不能在Java中交互的方式。

tadman的回答包含以下代码:

class CleanerClass
  def self.counter
    @counter ||= 0
  end

  def self.counter=(v)
    @counter = v.to_i
  end
end

对于世界上的Java(和C#)程序员来说,这段代码看起来不应该起作用。毕竟,在该模型中,从静态方法访问实例变量的想法是不连贯的(并且甚至不能编译)。重点是你还没有实例!

但是,在Ruby中,每个类定义本身都是Class类型的实例。对@counter的引用实际上在定义Class的{​​{1}}对象上创建了一个实例变量。将它更改为CleanerClass,就像你应该喜欢的那样,除了你声明的那个类之外,还会污染其他类。