“Can an object call a private method on another object of the same class?” or “How can you define class meth- ods by importing a module?”
中文意思是"一个对象可以调用同属一个类的其他对象的私有方法吗?或者"可以通过导入一个模块来创建类方法么?""
我猜想作者既然说这话,那么应该两句话都是肯定的才对吧。
第二句可以用 extend 这样的钩子证明。(之前我写的是 include,不对,include 的结果是实例方法)rails 中随处可见,举例
[98] pry(main)> module Mou
[98] pry(main)* def my_method
[98] pry(main)* 'my'
[98] pry(main)* end
[98] pry(main)* end
=> :my_method
[100] pry(main)> class C
[100] pry(main)* include Mou
[100] pry(main)* end
=> C
[101] pry(main)> class D < C
[101] pry(main)* end
=> nil
[104] pry(main)> D.new.my_method
=> "my"
[105] pry(main)> C.new.my_method
=> "my"
[106] pry(main)> class E
[106] pry(main)* extend Mou
[106] pry(main)* end
=> E
[108] pry(main)> E.my_method
=> "my"
[109] pry(main)>
```
但是,第一句话,有点迷惑,我的理解是`一个类A,的两个实例对象a1,a2,他们可以互相调用对方的私有方法。`对于一个学了老么长java的同学,理解起来真的很困难。即便我知道A也算是个对象,`但是,我也不明白为什么a1要调用,而且居然能调用a2的私有方法,难道这个私有方法不是共存在类中吗?`
这似乎也算多线程了吧。
追加第二句`一个对象的方法也是类的实例方法。一个类方法似乎就应该是class的实例方法`
那么如果我采用 extend moudel的方式话,module中的方法似乎就是类方法了,但却不是class的实例方法.
这个追问,我自己回答吧,这种情况,应该算作打开了一个类,然后追加方法。因此这些追加的方法应该是存在类中,而非class中。就好比你打开一个普通对象追加方法是一样的。(好像叫影子吧,不是很肯定,查查书再确定)
tip one:
```
在Java中,如果我们定义一个方法为私有方法,即用private关键字修饰,则此方法对子类是不可见的,子类不会知道超类中存在这样一个私有方法,自然也无法调用,如果我们试图访问,会收到错误信息。但是在Ruby中,这种情况改变了。如果我们在superclass中定义一个方法为private的私有方法,则其subclass依然可以继承此私有方法,在subclass内部可以访问此私有方法!这是和Java语言的机制截然不同的。
```
tip two:
```
我后来在《元编程》一书中发现这样一段文字
private methods come from two rules working together: first, you need an explicit receiver to call a method on an object that is not yourself, and second, private methods can be called only with an implicit receive.”
私有方法由两条规则一起控制: 第一条,如果调用方法的接收者不是自己,这必须明确指定接收者。第二条:私有方法只能被隐含接收者调用。
Can object x call a private method on object y if the two objects share the same class? The answer is no
如果对象X和对象Y都是同一个类的对象,则X能调用Y的私有方法吗?答案是“不能”(结合规则得出的)
```
tip three:
```
obj.send(:private)这种方式,不知道到底算不算
```
send 不就可以调用对象的私有方法吗?为啥要限定同一个类。等 meta programming 高手。
[1] pry(main)> class Klass
[1] pry(main)* private
[1] pry(main)* def test
[1] pry(main)* puts "private test"
[1] pry(main)* end
[1] pry(main)* end
=> :test
[2] pry(main)> a = Klass.new
=> #<Klass:0x00000101206d98>
[3] pry(main)> a.test
NoMethodError: private method `test' called for #<Klass:0x00000101206d98>
from (pry):8:in `__pry__'
[4] pry(main)> a.send(:test)
private test
=> nil
第一句话是对的,a,b 都是 A 的实例,那么在 a 的方法内部是可以调用 b 的私有方法的。这个写个 Java 程序验一下就知道了。
Java 的方法,都是随着类加载的,都在栈,整个系统都只有一份。简单来说,每个类都有一个 method lookup table,用来查询某个方法的入口地址在哪。当 a,b 不是同一个类时,a 只能查询到 b 的公开方法列表,所以,没法 binding 到 b 的私有方法。如果 a,b 同一个类,那么他们其实共用一个 lookup table,所以,就能找到私有方法入口。
这种调用私有方法跟多线程没有任何关系,对象的实例都是方法和变量分离的,变量可以简单理解为 C 里面的 Struct,通过一个指针引用。每次方法调用就是正常的进栈出栈。另外,java 虚拟机规范里面有介绍,当调用某个实例方法时,虚拟机会默认的把当前对象作为第一个参数压入栈,即如果调用 a.p_m(b),那么实际上是传了两个参数给方法 p_m,第一个参数是 a,也就是 p_m 方法内部的 this 引用,第二个参数是 b。所以,对于这样的代码。
class A(){
private String name;
public A(String name){
this.name = name;
}
public void m(A b){
p_m(b);
}
private void p_m(A b){
System.out.println("i am "+name);
b.p_m(a);
}
}
new A("a").m(new A("b"));
当调用 a.m 方法时,这是第一层栈;当调用 a.p_m 时,就是第二层的栈,这个栈内部的 this 是 a;当调用 b.p_m 时,就是第三层的栈,这个栈内部的 this 是 b。简单说,这就有点像递归,只是不断的压栈,然后让 CP 指回到方法入口,跟多线程没有任何关系。
#8 楼 @nickcen @blacktulip 我不认为 Ruby 成立
class A; end
a = A.new
b = A.new
def a.a_method; end
b.send(:a_method) # => NoMethodError
@neverlandxy_naix 你的代码不对啊,是在 a 的内部调用 b 的私有方法。直接这样调用私有方法,在哪个语言里面都不能够的。
# encoding:utf-8
class A
def initialize(name)
@name = name
end
def p(b)
m_p(b)
end
private
def m_p(b)
puts "inside #{@name}"
b.m_p(self)
end
end
A.new("a").p(A.new("b"))
#11 楼 @neverlandxy_naix #10 楼 @nickcen #7 楼 @blacktulip
无论怎样,都跑不通啊,java 的类和 ruby 的类,是两码事
#4 楼 @blacktulip 了解了. #10 楼 @nickcen 我这报错?ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-darwin12.0]
2.1.1 :016 >
2.1.1 :017 > A.new("a").p(A.new("b"))
inside a
NoMethodError: private method `m_p' called for #<A:0x0000010190a6f8 @name="b">
from (irb):13:in `m_p'
from (irb):7:in `p'
from (irb):17
from /Users/bob/.rvm/rubies/ruby-2.1.1/bin/irb:11:in `<main>'
提示在 a 里面调用了 b 的私有方法,好像不行呢?
#10 楼 @nickcen @ane 我无法理解怎么在 a 的里面调用 b 的私有方法,刚才写的代码是有误,我写了个新的
这个 b_method
方法,就是只属于 b 的私有方法,定义在 b 的 eigenclass 中,而在 class A 里面定义的私有方法,是这个 class A 的所有实例的私有方法,即如果在 class A 中定义私有方法,那这个私有方法就是 实例 a 和 实例 b 共有的私有方法
class A; end
a = A.new
b = A.new
class << b
private
def b_method; end
end
p b.private_methods.grep(/b_method/) # => [:b_method]
a.send(:b_method) # => NoMethodError
#14 楼 @neverlandxy_naix make sense. 这句话语义上怪怪的,不知道怎样定义那个私有方法才算是满足前提条件,这样 a,b 的 class 都是 A,算是同一个类的其他对象了,但是无法 call.
public class Main {
private String name;
public Main(String name){
this.name = name;
}
public void m (Main b){
p_m(b);
}
private void p_m (Main b){
System.out.println("i am "+name);
b.p_m(a);
}
public static void main(String[] args) {
// write your code here
Main a = new Main("a");
Main b = new Main("b");
a.p_m(b);
}
}
这么写出来的代码也不对啊。编译都通不过的,哈哈
Error:(13, 15) java: cannot find symbol
symbol: variable a
location: class com.company.Main
ps:我写的时候一会忘记分号,一会忘记写参数类型。
class A
def call_pri(obj)
obj.send 'pri'
end
private
def pri;end
end
a = A.new
b = A.new
a.call_pri b
我是这样理解的
#14 楼 @neverlandxy_naix #13 楼 @debugger #1 楼 @blacktulip #6 楼 @nickcen #17 楼 @5swords
各位:这个算吗
[91] pry(main)> class A
[91] pry(main)* def initialize(name)
[91] pry(main)* @name = name
[91] pry(main)* end
[91] pry(main)* def call_pri(obj)
[91] pry(main)* obj.send(:pri)
[91] pry(main)* end
[91] pry(main)* private
[91] pry(main)* def pri
[91] pry(main)* @name
[91] pry(main)* end
[91] pry(main)* end
=> :pri
[92] pry(main)> a = A.new('a')
=> #<A:0x000001014b17a0 @name="a">
[93] pry(main)> b = A.new('b')
=> #<A:0x000001015e6e40 @name="b">
[94] pry(main)> a.call_pri b
=> "b"
@ane, 这个不算。public method 里面可以随意使用 private methods, 毕竟 private methods 的主要目的之一就是为了 public methods 服务的。你可以直接def call_pri; pri; end
,都没有必要 send。
我觉得你没有必要为了这一句话纠结,等看到了具体代码部分再详细讨论比较有价值。
前提没有说这个私有方法是不是 2 个都有或者都没有。这个私有方法放在 A(Class 的实例) 这个类实例里面的,方法是共享的,#22 楼 @neverlandxy_naix 如果进入 b 的元类定义就产生了特征类 (鬼魂类,单例类),这样岂不是算不上同一个类的不同对象了?不知道这样理解对不对。
太囧了。
#26 楼 @neverlandxy_naix 也许你是对的,我看元编程里面有这么一句。
Ruby 中每个对象都有其自己的匿名类,一个类能拥有方法,但是只能对该对象本身其作用:当我们对一个具体的对象添加方法时,Ruby 会插入一个新的匿名类于父类之间,来容纳这个新建立的方法。
#26 楼 @neverlandxy_naix #24 楼 @debugger 我觉得作者的原意应该就是在 a 的方法中调用 b.send(:private)。而不应该是所谓的 a.b_private 或者它的变形 a.send(b_private) 等等
ruby 这种调用是不能这样调用的。
java 是可以的。之前代码只是示意。可编译和 run 的代码是这样的。
package test;
public class Main {
public static void main(String[] args){
new A("a").m1(new A("b"));
}
}
-------------------
package test;
public class A {
private String name;
public A(String name) {
this.name = name;
}
public void m1(A b){
m2(b);
}
private void m2(A b) {
System.out.println("inside "+name);
if (b != null) {
b.m2(null);
}
}
}
public void m1(A b){
b.m1(null);
}
不对。后来看到错误提示java.lang.NullPointerException
,于是我似乎想起什么了
#32 楼 @blacktulip 没,昨天想重看一下《元编程》,所以遇到疑惑的就贴出来,看看别人这么看的。学而思嘛。其实也没太费心思。至少在这过程中我发现,原来 include module 的本质是给类添加了一个父类,等等一系列被以前一眼带过的知识,到写 rails 的时候,发现很多问题都是盲目的写
@ane 回答: 追加第二句一个对象的方法也是类的实例方法。一个类方法似乎就应该是 class 的实例方法 那么如果我采用 extend moudel 的方式话,module 中的方法似乎就是类方法了,但却不是 class 的实例方法。
这里应该是 singleton methods 吧。感觉 singleton methods 是介于 class methods 和 instance methods 之间的东东,而且不能 instatilize.