Я просто заметил, что Вы сослались моя статья .
, Спецификация Java говорит, что все в Java - передача значением. Нет такой вещи как "передача ссылкой" в Java.
ключ к пониманию это - то, что что-то как [1 145]
Dog myDog;
не Собака; это на самом деле указатель Собаке.
то, Что это означает, когда Вы имеете
Dog myDog = new Dog("Rover");
foo(myDog);
, Вы чрезвычайно передающие адрес из созданных Dog
объект к foo
метод.
(я говорю по существу, потому что указатели Java не являются прямыми адресами, но является самым легким думать о них тот путь)
предположим эти Dog
, объект находится в адресе памяти 42. Это означает, что мы передаем 42 методу.
, если Метод был определен как [1 151]
public void foo(Dog someDog) {
someDog.setName("Max"); // AAA
someDog = new Dog("Fifi"); // BBB
someDog.setName("Rowlf"); // CCC
}
, позволяют нам посмотреть на то, что происходит.
someDog
устанавливается на значение 42 someDog
, сопровождается к Dog
, это указывает на (эти Dog
объект в адресе 42) Dog
(тот в адресе 42) попросился изменить его имя на MaxDog
создается. Скажем, он в адресе 74 someDog
74 Dog
, это указывает на (эти Dog
объект в адресе 74) Dog
(тот в адресе 74) попросился изменить его имя на RowlfТеперь, давайте думать о том, что происходит вне метода:
Сделал myDog
изменение?
существует ключ.
Учет того факта, который myDog
указатель , и не фактическое Dog
, ответ, НЕТ. myDog
все еще имеет значение 42; это все еще указывает на оригинал Dog
(но обратите внимание, что из-за строки "AAA", его именем является теперь "Max" - все еще та же Собака; myDog
значение не изменилось.)
Это совершенно допустимо к [1 131], следуют адрес и изменяют то, что в конце его; это не заменяет переменную, как бы то ни было.
Java работает точно как C. Можно присвоить указатель, передать указатель на метод, следовать за указателем в методе и изменить данные, на которые указали. Однако Вы не можете измениться, где тот указатель указывает.
В C++, Ada, Паскале и других языках, которые поддерживают передачу ссылкой, можно на самом деле заменить переменную, которая была передана.
, Если бы Java имел семантику передачи ссылкой, foo
метод, мы определили выше, изменился бы, где myDog
указывал, когда это присвоилось someDog
на строке BBB.
Думают о параметрах ссылки, как являющихся псевдонимами для переменной, переданной в. Когда тот псевдоним присвоен, так переменная, которая была передана в.
Рубиновым эквивалентом, который не является идиоматическим, будет:
def my_callback(a, b, c, status_code)
puts "did stuff with #{a}, #{b}, #{c} and got #{status_code}"
end
def do_stuff(a, b, c, callback)
sum = a + b + c
callback.call(a, b, c, sum)
end
def main
a = 1
b = 2
c = 3
do_stuff(a, b, c, method(:my_callback))
end
Идиоматический подход заключается в передаче блока вместо ссылки на метод. Одним из преимуществ блока перед автономным методом является контекст: блок - это замыкание , поэтому он может ссылаться на переменные из области, в которой он был объявлен. Это сокращает количество параметров, которые do_stuff необходимо передать обратному вызову. Например:
def do_stuff(a, b, c, &block)
sum = a + b + c
yield sum
end
def main
a = 1
b = 2
c = 3
do_stuff(a, b, c) { |status_code|
puts "did stuff with #{a}, #{b}, #{c} and got #{status_code}"
}
end
This "idiomatic block" is a very core part of everyday Ruby and is covered frequently in books and tutorials. The Ruby information section provides links to useful [online] learning resources.
The idiomatic way is to use a block:
def x(z)
yield z # perhaps used in conjunction with #block_given?
end
x(3) {|y| y*y} # => 9
Or perhaps converted to a Proc; here I show that the "block", converted to a Proc implicitly with &block
, is just another "callable" value:
def x(z, &block)
callback = block
callback.call(z)
end
# look familiar?
x(4) {|y| y * y} # => 16
(Only use the above form to save the block-now-Proc for later use or in other special cases as it adds overhead and syntax noise.)
However, a lambda can be use just as easily (but this is not idiomatic):
def x(z,fn)
fn.call(z)
end
# just use a lambda (closure)
x(5, lambda {|y| y * y}) # => 25
While the above approaches can all wrap "calling a method" as they create closures, bound Methods can also be treated as first-class callable objects:
class A
def b(z)
z*z
end
end
callable = A.new.method(:b)
callable.call(6) # => 36
# and since it's just a value...
def x(z,fn)
fn.call(z)
end
x(7, callable) # => 49
In addition, sometimes it's useful to use the #send
method (in particular if a method is known by name). Here it saves an intermediate Method object that was created in the last example; Ruby is a message-passing system:
# Using A from previous
def x(z, a):
a.__send__(:b, z)
end
x(8, A.new) # => 64
Happy coding!
Так что, это может быть очень "нерубиново", и я не "профессиональный" Ruby-разработчик, так что если вы, ребята, собираетесь шлепнуть, будьте осторожны, пожалуйста :)
Ruby имеет встроенный модуль под названием Observer. Я не нашел его простым в использовании, но, честно говоря, я не дал ему особого шанса. В своих проектах я прибегал к созданию собственного типа EventHandler (да, я много использую C #). Вот основная структура:
class EventHandler
def initialize
@client_map = {}
end
def add_listener(id, func)
(@client_map[id.hash] ||= []) << func
end
def remove_listener(id)
return @client_map.delete(id.hash)
end
def alert_listeners(*args)
@client_map.each_value { |v| v.each { |func| func.call(*args) } }
end
end
Итак, чтобы использовать это, я выставляю его как член класса, доступный только для чтения:
class Foo
attr_reader :some_value_changed
def initialize
@some_value_changed = EventHandler.new
end
end
Клиенты класса «Foo» могут подписаться на событие, подобное этому:
foo.some_value_changed.add_listener(self, lambda { some_func })
Я уверен, что это не является идиоматическим Ruby, и я просто переношу свой опыт работы с C # на новый язык, но у меня это сработало.
Я часто реализую обратные вызовы в Ruby, как в следующем примере. Это очень удобно в использовании.
class Foo
# Declare a callback.
def initialize
callback( :on_die_cast )
end
# Do some stuff.
# The callback event :on_die_cast is triggered.
# The variable "die" is passed to the callback block.
def run
while( true )
die = 1 + rand( 6 )
on_die_cast( die )
sleep( die )
end
end
# A method to define callback methods.
# When the latter is called with a block, it's saved into a instance variable.
# Else a saved code block is executed.
def callback( *names )
names.each do |name|
eval <<-EOF
@#{name} = false
def #{name}( *args, &block )
if( block )
@#{name} = block
elsif( @#{name} )
@#{name}.call( *args )
end
end
EOF
end
end
end
foo = Foo.new
# What should be done when the callback event is triggered?
foo.on_die_cast do |number|
puts( number )
end
foo.run