java Java中“私有静态最终”和“公共静态最终”类变量的最接近的Ruby表示?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2441524/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 21:09:38  来源:igfitidea点击:

Closest Ruby representation of a 'private static final' and 'public static final' class variable in Java?

javarubystaticprivatepublic

提问by Hosh

Given the Java code below, what's the closest you could represent these two static finalvariables in a Ruby class? And, is it possible in Ruby to distinguish between private staticand public staticvariables as there is in Java?

给定下面的 Java 代码,您可以static final在 Ruby 类中最接近地表示这两个变量吗?而且,在 Ruby 中是否可以像在 Java 中那样区分private staticpublic static变量?

public class DeviceController
{
  ...
  private static final Device myPrivateDevice = Device.getDevice("mydevice");
  public static final Device myPublicDevice = Device.getDevice("mydevice");
  ...
  public static void main(String args[])
  {
   ...
  }
}

回答by J?rg W Mittag

There really is no equivalent construct in Ruby.

Ruby 中确实没有等效的构造。

However, it looks like you are making one of the classic porting mistakes: you have a solutionin language A and try to translate that into language B, when what you really should do is figure out the problemand then figure out how to solve it in language B.

但是,您似乎正在犯一个经典的移植错误:您在语言 A 中有一个解决方案,并尝试将其翻译成语言 B,而您真正应该做的是找出问题,然后找出如何解决它在 B 语言中。

I can't really be sure what the problem is you are trying to solve from that small codesnippet, but here is onepossible idea for how to implement it in Ruby:

我真的不能确定你想从那个小代码片段中解决什么问题,但这里有一个关于如何在 Ruby 中实现它的可能想法:

class DeviceController
  class << self
    def my_public_device;  @my_public_device  ||= Device['mydevice'] end

    private

    def my_private_device; @my_private_device ||= Device['mydevice'] end
  end
end

Here's another:

这是另一个:

class DeviceController
  @my_public_device  ||= Device['mydevice']
  @my_private_device ||= Device['mydevice']

  class << self
    attr_reader :my_public_device, :my_private_device
    private :my_private_device
  end
end

(The difference is that the first example is lazy, it only initializes the instance variable when the corresponding attribute reader is first called. The second one initializes them as soon as the class body is executed, even if they are never needed, just like the Java version does.)

(区别在于第一个例子是惰性的,它只在第一次调用相应的属性读取器时才初始化实例变量。第二个在类体执行后立即初始化它们,即使它们永远不需要,就像Java 版本可以。)

Let's go over some of the concepts here.

让我们回顾一下这里的一些概念。

In Ruby, as in every other "proper" (for various definitions of "proper") object-oriented language, state (instance variables, fields, properties, slots, attributes, whatever you want to call them) is alwaysprivate. There is no wayto access them from the outside. The only way to communicate with an object is by sending it messages.

在 Ruby 中,就像在所有其他“正确”(对于“正确”的各种定义)面向对象语言中一样,状态(实例变量、字段、属性、槽、属性,无论您想如何称呼它们)始终是私有的。有没有办法从外部访问它们。与对象通信的唯一方法是向它发送消息。

[Note: Whenever I write something like "no way", "always", "the only way" etc., it actually no means "no way, except for reflection". In this particular case, there is Object#instance_variable_set, for example.]

[注意:每当我写“不可能”,“总是”,“唯一的方法”等内容时,实际上并不意味着“除了反思之外”。例如,在这种特殊情况下,有Object#instance_variable_set。]

In other words: in Ruby, variables are always private, the only way to access them is via a getter and/or setter method, or, as they are called in Ruby, an attribute reader and/or writer.

换句话说:在 Ruby 中,变量始终是私有的,访问它们的唯一方法是通过 getter 和/或 setter 方法,或者,如它们在 Ruby 中所称的,属性读取器和/或写入器。

Now, I keep writing about instance variables, but in the Java example we have static fields, i.e. classvariables. Well, in Ruby, unlike Java, classes are objects, too. They are instances of the Classclass and so, just like any other object, they can have instance variables. So, in Ruby, the equivalent to a class variable is really just a standard instance variable which belongs to an object which just happens to be a class.

现在,我继续写实例变量,但在 Java 示例中,我们有静态字段,即变量。好吧,在 Ruby 中,与 Java 不同的是,类也是对象。它们是Class类的实例,因此,就像任何其他对象一样,它们可以具有实例变量。因此,在 Ruby 中,类变量的等价物实际上只是一个标准实例变量,它属于恰好是类的对象。

(There are also class hierarchy variables, denoted with a double at sign @@sigil. Those are really weird, and you should probably just ignore them. Class hierarchy variables are shared across the entire class hierarchy, i.e. the class they belong to, all its subclasses and their subclasses and their subclasses ... and also all instances of all of those classes. Actually, they are more like global variables than class variables. They should really be called $$varinstead of @@var, since they are much more closely related to global variables than instance variables. They are not entirely useless but only very rarely useful.)

(还有类层次变量,用双符号表示@@sigil。这些真的很奇怪,你可能应该忽略它们。类层次变量在整个类层次结构中共享,即它们所属的类,它的所有子类和它们的子类和它们的子类......以及所有这些类的所有实例。实际上,它们更像是全局变量而不是类变量。实​​际上应该调用它们$$var而不是@@var,因为它们与全局变量的关系比实例更密切变量。它们并非完全没用,只是很少有用。)

So, we have covered the "field" part (Java field == Ruby instance variable), we have covered the "public" and "private" parts (in Ruby, instance variables are always private, if you want to make them public, use a public getter/setter method) and we have covered the "static" part (Java static field == Ruby class instance variable). What about the "final" part?

因此,我们已经涵盖了“字段”部分(Java 字段 == Ruby 实例变量),我们已经涵盖了“公共”和“私有”部分(在 Ruby 中,实例变量始终是私有的,如果您想将它们设为公开,使用公共 getter/setter 方法),我们已经涵盖了“静态”部分(Java 静态字段 == Ruby 类实例变量)。“最后”部分呢?

In Java, "final" is just a funny way of spelling "const", which the designers avoided because the constkeyword in languages like C and C++ is subtly broken and they didn't want to confuse people. Ruby doeshave constants (denoted by starting with a capital letter). Unfortunately, they are not really constant, because trying to modify them, while generating a warning, actually works. So, they are more of a convention than a compiler-enforced rule. However, the more important restriction of constants is that they are always public.

在 Java 中,“final”只是“const”的一种有趣的拼写方式,设计者避免使用这种方式,因为constC 和 C++ 等语言中的关键字被巧妙地破坏了,他们不想混淆人们。Ruby确实有常量(以大写字母开头)。不幸的是,它们并不是真正恒定的,因为尝试修改它们,同时生成警告,实际上是有效的。因此,它们更像是一种约定,而不是编译器强制执行的规则。然而,常量更重要的限制是它们总是公开的。

So, constants are almost perfect: they cannot be modified (well, they shouldn'tbe modified), i.e. they are final, they belong to a class (or module), i.e. they are static. But they are always public, so unfortunately they cannot be used to model private static finalfields.

所以,常量几乎是完美的:它们不能被修改(好吧,它们不应该被修改),即它们是final,它们属于一个类(或模块),即它们是static。但它们总是public,所以不幸的是它们不能用于建模private static final字段。

And this is exactly the point where thinking about problems instead of solutions comes in. What is it that you want? You want state that

这正是考虑问题而不是解决方案的关键。你想要什么?你想说明

  1. belongs to a class,
  2. can only be read not written,
  3. is only initialized once and
  4. can be either private or public.
  1. 属于一个类,
  2. 只能读不能写
  3. 只初始化一次并且
  4. 可以是私有的,也可以是公共的。

You can achieve all of that, but in a completely different way than in Java:

您可以实现所有这些,但方式与 Java 完全不同:

  1. class instance variable
  2. don't provide a setter method, only a getter
  3. use Ruby's ||=compound assignment to assign only once
  4. getter method
  1. 类实例变量
  2. 不提供setter方法,只提供getter
  3. 使用 Ruby 的||=复合赋值只赋值一次
  4. 吸气方法

The only thing you have to worry about, is that you don't assign to @my_public_deviceanywhere, or better yet, don't access it at all. Always use the getter method.

您唯一需要担心的是,您不要分配到@my_public_device任何地方,或者更好的是,根本不要访问它。始终使用 getter 方法。

Yes, this isa hole in the implementation. Ruby is often called a "grown-up's language" or a "consenting adults language", which means that instead of having the compiler enforce certain things, you just put them in the documentation and simply trust that your fellow developers have learned that touching other people's privates is rude ...

是的,这实施中的一个漏洞。Ruby 通常被称为“成年人的语言”或“同意的成人语言”,这意味着您不必让编译器强制执行某些事情,而只需将它们放在文档中,并简单地相信您的开发伙伴已经学会了接触其他人人们的私处很粗鲁……



A totally differentapproach to privacy is the one used in functional languages: use closures. Closures are blocks of code that close over their lexical environment, even after that lexical environment has gone out of scope. This method of implementing private state is very popular in Scheme, but has recently also been popularized by Douglas Crockford et al. for JavaScript. Here's an example in Ruby:

一种完全不同的隐私方法是在函数式语言中使用的方法:使用闭包。闭包是关闭其词法环境的代码块,即使在该词法环境超出范围之后也是如此。这种实现私有状态的方法在 Scheme 中非常流行,但最近也被 Douglas Crockford 等人推广。对于 JavaScript。这是 Ruby 中的一个示例:

class DeviceController
  class << self
    my_public_device, my_private_device = Device['mydevice'], Device['mydevice']

    define_method :my_public_device  do my_public_device  end
    define_method :my_private_device do my_private_device end

    private :my_private_device
  end # <- here the variables fall out of scope and can never be accessed again
end

Note the subtle but important difference to the versions at the top of my answer: the lack of the @sigil. Here, we are creating localvariables, not instancevariables. As soon as the class body ends, those local variables fall out of scope and can never be accessed ever again. Onlythe two blocks which define the two getter methods still have access to them, because they close over the class body. Now, they are reallyprivate andthey are final, because the only thing in the entire program which still has access to them is a pure gettermethod.

请注意与我的答案顶部的版本的微妙但重要的区别:缺少@印记。在这里,我们创建的是局部变量,而不是实例变量。一旦类体结束,这些局部变量就会超出范围并且永远无法再次访问。只有定义两个 getter 方法的两个块仍然可以访问它们,因为它们关闭了类主体。现在,它们真的是私有的,而且它们是私有的final,因为整个程序中唯一仍然可以访问它们的是纯getter方法。

This is probably not idiomatic Ruby, but for anyone with a Lisp or JavaScript background it should be clear enough. It is also very elegant.

这可能不是惯用的 Ruby,但对于任何具有 Lisp 或 JavaScript 背景的人来说,这应该足够清楚了。它也非常优雅。

回答by Mark Rushakoff

The closest thing I can think of to a final variable is to put the variable in question as an instance variable of a module:

我能想到的最接近 final 变量的是将有问题的变量作为模块的实例变量:

class Device
    # Some static method to obtain the device
    def self.get_device(dev_name)
        # Need to return something that is always the same for the same argument
        dev_name
    end
end

module FinalDevice
    def get_device
        # Store the device as an instance variable of this module...
        # The instance variable is not directly available to a class that
        # includes this module.
        @fin ||= Device.get_device(:my_device).freeze
    end
end

class Foo
    include FinalDevice
    def initialize
        # Creating an instance variable here to demonstrate that an
        # instance of Foo cannot see the instance variable in FinalDevice,
        # but it can still see its own instance variables (of course).
        @my_instance_var = 1
    end
end

p Foo.new

p (Foo.new.get_device == Foo.new.get_device)

This outputs:

这输出:

#<Foo:0xb78a74f8 @my_instance_var=1>
true

The trick here is that by encapsulating the device into a module, you can only access the device through that module. From the class Foo, there's no way to modify whichdevice you're accessing, without directly acting upon the Deviceclass or the FinalDevicemodule. The freezecall in FinalDevicemay or may not be appropriate, depending on your needs.

这里的技巧是通过将设备封装到一个模块中,您只能通过该模块访问该设备。从类Foo,有没有办法改变你所访问设备,而无需对直接作用的Device类或FinalDevice模块。该freeze呼叫FinalDevice可能会或可能不适合,根据您的需要。

If you want to make a public and private accessor, you can modify Foolike this:

如果要创建公共和私有访问器,可以Foo像这样修改:

class Foo
    include FinalDevice

    def initialize
        @my_instance_var = 1
    end

    def get_device_public
        get_device
    end

    private
    def get_device_private
        get_device
    end

    private :get_device
end

In which case you'll probably need to modify FinalDevice::get_deviceto take an argument as well.

在这种情况下,您可能还需要修改FinalDevice::get_device以接受参数。

Update: @banister has pointed out that @finas declared in FinalDeviceis indeed accessible by an instance of Foo. I had lazily assumed that since it wasn't in the default text output by Foo#inspect, it wasn't inside Foo.

更新:@banister指出,@fin在声明FinalDevice的确是由一个实例访问Foo。我懒洋洋地认为,因为它不在 的默认文本输出中Foo#inspect,所以它不在Foo.

You can remedy this by more explicitly making @finan instance variable of the FinalDevicemodule:

您可以通过更明确地创建模块@fin的实例变量来解决此问题FinalDevice

class Device
    def self.get_device(dev_name)
        dev_name
    end
end

module FinalDevice
    def get_device
        FinalDevice::_get_device
    end

    protected
    def self._get_device
        @fin ||= Device.get_device(:my_device).freeze
    end
end

class Foo
    include FinalDevice

    def get_device_public
        get_device
    end

    def change_fin
        @fin = 6
        @@fin = 8
    end

    private
    def get_device_private
        get_device
    end

    private :get_device
end

f = Foo.new
x = f.get_device_public
f.change_fin
puts("fin was #{x}, now it is #{f.get_device_public}")

Which correctly outputs:

哪个正确输出:

fin was my_device, now it is my_device

回答by alex.zherdev

class DeviceController
  MY_DEVICE = Device.get_device("mydevice")
end

And yeah, require 'device'if needed.

是的,require 'device'如果需要的话。

Although nothing will stop you from redefining the constant somewhere else, except a warning :)

虽然没有什么能阻止你在其他地方重新定义常量,除了警告:)

回答by horseyguy

private static in Ruby:

Ruby 中的私有静态:

class DeviceController
    @@my_device = Device.get_device("mydevice")
end

public static in Ruby:

Ruby 中的公共静态:

class DeviceController       
    def self.my_device; @@my_device; end

    @@my_device = Device.get_device("mydevice")
end

Ruby can have no 'final' :)

Ruby 不能有 'final' :)