《Rails for Java Developers》读书笔记1

书名:《Rails for Java Developers》英文版PDF
所处章节:第二章 Programming Ruby
页数范围:38~71

Java/Ruby语法特性对比:
Java divides the world into primitive types and objects. The primitive
types represent numeric values of various ranges and precision:
int i;


primitive types can use infix math, for example:
1+1;


To represent arbitrarily large integers, Java uses the BigInteger class:
BigInteger twobil = new BigInteger("2000000000");


BigInteger cannot use infix mathematical notation you have
to say:
a.multiply(b)


Ruby transparently uses
the appropriate type as needed, Notice that x smoothly shifts from Fixnum to Bignum as necessary:
irb(main):004:0> x = 10**9
=> 1000000000
irb(main):005:0> x.class
=> Fixnum
irb(main):006:0> x *= 100
=> 100000000000
irb(main):007:0> x.class
=> Bignum

irb(main):018:0> 1.0.finite?
=> true
irb(main):019:0> (1.0/0.0).finite?
=> false


"?" by convention it is used for methods that return a
boolean

print(String.format("Hello, %s", name.toUpperCase()));

irb(main):005:0> "Hello, #{name.upcase}"
=> "Hello, READER"


In Java, you can use backslash escapes to represent characters:
print(String.format("Hello, \"%s\"\nWelcome to Java", name));

Ruby also uses backslash escapes:
irb(main):008:0> puts "Hello, \"#{name}\"\nWelcome to Ruby"
Hello, "Reader"
Welcome to Ruby


with %Q you can get rid of all these backslash escapes:
irb(main):011:0> puts %Q{"One", "Two", and "Three" are strings"}
"One", "Two", and "Three" are strings"


assertEquals('H', "Hello".charAt(0));

irb(main):015:0> ?A
=> 65
irb(main):019:0> ?H == "Hello".slice(0)
=> true
irb(main):022:0> ?o == "Hello"[-1]
=> true
irb(main):025:0> "Hello"[1,4]
=> "ello"


To turn off string interpolation,use a single-quoted string instead of a double-quoted one:
irb(main):028:0> "Hello, #{name.upcase}"
=> "Hello, READER"
irb(main):029:0> 'Hello, #{name.upcase}'
=> "Hello, \#{name.upcase}"


Java:
print("one\ntwo\nthree");

Ruby provides an explicit syntax for multiline strings called a here
document, or heredoc.
irb(main):035:0> puts <<MY_DELIMITER
irb(main):036:0" one
irb(main):037:0" two
irb(main):038:0" three
irb(main):039:0" MY_DELIMITER
one
two
three



Java:
public static String bleep(String input) {
return input.replaceAll("\\b\\w{4}\\b", "(bleep)");
}

Ruby use regular expressions, delimiting them with //:
irb(main):041:0> 'Are four letter words mean?'.gsub(/\b\w{4}\b/, "(bleep)")
=> "Are (bleep) letter words (bleep)?"


print("HELLO".toLowerCase());

irb(main):047:0> "HELLO".downcase()
=> "hello"
irb(main):048:0> "HELLO".downcase
=> "hello"


print(Math.cos(0));

irb(main):051:0> Math.cos(0)
=> 1.0
irb(main):051:0> Math.cos 0
=> 1.0


In Java, objects are type-safe. Objects know what they are capable of, type-safe and you cannot ask them to perform methods they do not have:
print("hello".cos(0));

Ruby objects are also type-safe:
irb(main):057:0> "hello".cos 0
NoMethodError: undefined method ‘cos' for "hello":String
from (irb):57


Type Safety Does Not Ensure Correctness!!!

public class PrintArgs {
public static void main(String[] args) {
for (int n=0; n<args.length; n++) {
System.out.println(args[n]);
}
}
}

ARGV.each {|x| puts x}
or
ARGV.each do |x|
puts x
end

$ ruby samples/print_args.rb one two three
one
two
three

irb(main):009:0> ['C', 'Java', 'Ruby'].each {|lang| puts "#{lang} is fun!"}
C is fun!
Java is fun!
Ruby is fun!
=> ["C", "Java", "Ruby"]

irb(main):002:0> ['C', 'Java', 'Ruby'] == %w{C Java Ruby}
=> true

irb(main):002:0> [1,2] + [3]
=> [1, 2, 3]
irb(main):003:0> [1,2,3] * 2
=> [1, 2, 3, 1, 2, 3]
irb(main):004:0> [1,2,1] - [2]
=> [1, 1]
irb(main):005:0> [1,2] / 2
NoMethodError: undefined method ‘/' for [1, 2]:Array
from (irb):5

irb(main):006:0> skills = ['C', 'Java']
=> ["C", "Java"]
irb(main):007:0> skills.push 'Ruby'
=> ["C", "Java", "Ruby"]
irb(main):008:0> skills.pop
=> "Ruby"


import java.util.*;
public class PrintEnv {
public static void main(String[] args) {
Map map = System.getenv();
for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
Map.Entry e = (Map.Entry) it.next();
System.out.println(String.format("%s: %s", e.getKey(), e.getValue()));
}
}
}

ENV.each {|k,v| puts "#{k}: #{v}"}

irb(main):037:0> dict = {:do => "a deer", :re => "a drop of golden sun"}
=> {:do=>"a deer", :re=>"a drop of golden sun"}

irb(main):013:0> dict.fetch(:do)
=> "a deer"
irb(main):014:0> dict.store(:so, "a needle pulling thread")
=> "a needle pulling thread"

irb(main):015:0> dict[:so]
=> "a needle pulling thread"
irb(main):016:0> dict[:dos] = "a beer"
=> "a beer"


import java.util.Map;
public class ForEach {
public static void main(String[] args) {
for (String arg: args) {
System.out.println(arg);
}
for (Map.Entry entry: System.getenv().entrySet()) {
System.out.println(
String.format("%s : %s", entry.getKey(), entry.getValue()));
}
}
}

irb(main):017:0> [1,2,3,4,5].collect {|x| x**2}
=> [1, 4, 9, 16, 25]

irb(main):021:0> [1,2,3,4,5].find_all {|x| x%2==0}
=> [2, 4]


We want to make two important points from these examples:
• Languages evolve and improve over time. Usually improvement
comes not from thin air but from ideas that have already been
explored elsewhere. Java’s For-Each syntax was inspired by other
languages that have similar features. The programmers who facilitate
this kind of cross-pollination are those who become fluent in
multiple programming languages.
• Languages evolve at many levels. Runtimes can change, language
syntax can change, and libraries can change. In Java, iteration
changes like the addition of For-Each are language changes. Similar
changes in Ruby are library changes, since each et. al. are
method calls. Library changes are easier to make than language
changes. (Many developers write libraries, few developers write
languages, and language evolution tends to be retarded by standards
bodies, backward compatibility, and so on.) Merely pointing
out that Java and Ruby enable different approaches to change is
not a value judgment. However, it may lead to value judgments
in a specific context (which you must provide). What parts of your
system need to change? On what timeline, and under whose direction?
Conversely, what parts need to stay rock-solid stable and be
guaranteed to work in the same fashion across different projects
over time?

if (n > 5) {
print("big");
} else {
print("little");
}

if n>5
puts "big"
else
puts "little"
end

or

puts(if (n>5)
"big"
else
"little"
end)


if (o != null) {
print("got o");
}

o = nil
puts o ? true : false

if lines > 1; puts "you have more than one line"; end

puts "you have more than one line" if lines > 1

puts "you've got lines" if lines != 0
puts "you've got lines" unless lines == 0

The following program runs an input loop, shouting back everything passed via stdin:
line = ""
puts "Shouting back #{line.upcase}" while line=gets


The ugliness here comes from using the boolean operator || to shoehorn
two statements into one to conform to the requirements of the statement
modifier form.
irb(main):026:0> i=1; puts(i*i) || i+=1 while i<=5
1
4
9
16
25

(1..5).each {|x| puts x*x}

irb(main):014:0> (1..10).max
=> 10
irb(main):015:0> (1...10).max
=> 9
irb(main):016:0> (1..10).exclude_end?
=> false
irb(main):017:0> (1...10).exclude_end?
=> true

("A".."C").each {|x| puts x*5}
AAAAA
BBBBB
CCCCC

irb(main):003:0> ('A'..'I').step(2) {|x| print x}
ACEGI


public static int numberGrade(char letter) {
switch(letter) {
case 'a':
return 100;
case 'b':
return 90;
case 'c':
return 80;
case 'd':
return 70;
case 'e':
case 'f':
return 0;
}
return 0;
}

def number_grade(letter)
case letter
when 'A': 100
when 'B': 90
when 'C': 80
when 'D': 70
else 0
end
end

def letter_grade(x)
case x
when 90..100: 'A'
when 80..90: 'B'
when 70..80: 'C'
when 60..70: 'D'
when Integer: 'F'
when /[A-F]/: x
else raise "not a grade: #{x}"
end
end

irb(main):018:0> case('John')
irb(main):019:1> when('John'): 'a name'
irb(main):020:1> when(String): 'a word'
irb(main):021:1> end
=> "a name"

You can also invoke the case equality operator directly; it is written as
a triple equals (===):
irb(main):002:0> (90..100) === 95
=> true
irb(main):003:0> (90..100) === 95.5
=> true
irb(main):004:0> (90..100) === 0
=> false
irb(main):005:0> Integer === 95
=> true
irb(main):006:0> Integer === 95.5
=> false


public class Person {
private String firstName;
private String lastName;

class Person

That’s it. There is no need to declare instance variables (the Ruby equivalent
of fields) because they come into existence automatically when
they are used.Ruby instance variables are
implicitly private, so this designation is unneeded as well.

Defining Constructors
In Java, Notice that the constructor
arguments share the same names as the private fields: firstName and
lastName. To disambiguate, you explicitly prefix the instance variables
with this.
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

In Ruby, instance
variables must begin with a @, so there is no danger of a name collision
with method parameters.
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end


public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFullName() {
return String.format("%s %s", firstName, lastName);
}

attr_accessor :first_name, :last_name
def full_name
"#{first_name} #{last_name}"
end

Ruby also provides attr_reader and attr_writer if you want read-only
or write-only properties.

p.first_name = 'Justin'
p.last_name = 'Gehtland'
puts "Hello from #{p.first_name}"


To a Java eye, this looks like direct access to the fields first_name and
last_name, but in Ruby these are method calls. Even the punctuation (=)
is part of a method name. To make this clearer, here is a hand-coded
version of the accessors:
# don't do this--use attr_accessor!
def first_name
@first_name
end
def first_name=(new_name)
@first_name = new_name
end


public void marry(Person other) {
String newLastName =
String.format("%s-%s", getLastName(), other.getLastName());
setLastName(newLastName);
other.setLastName(newLastName);
}

def marry(other)
other.last_name = self.last_name = "#{self.last_name}-#{other.last_name}"
end

If you forget to prefix a setter with self, you may create hard-to-find
bugs. Java does not suffer from this ambiguity, so be careful.


Sometimes methods apply to a class as a whole, instead of to any particular
instance of a class. In Java these are called static methods:
public static String getSpecies() {
return "Homo sapiens";
}

In Ruby, these methods are called class methods:
def self.species
"Homo sapiens"
end


public class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFullName() {
return String.format("%s %s", firstName, lastName);
}
public void marry(Person other) {
String newLastName =
String.format("%s-%s", getLastName(), other.getLastName());
setLastName(newLastName);
other.setLastName(newLastName);
}
public static String getSpecies() {
return "Homo sapiens";
}
}

class Person
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
attr_accessor :first_name, :last_name
def full_name
"#{first_name} #{last_name}"
end
def marry(other)
other.last_name = self.last_name = "#{self.last_name}-#{other.last_name}"
end
def self.species
"Homo sapiens"
end
end



The == operator tests for identity. Strings s1 and s2 are == because they
point to the same object. Strings3 is not== to the others. It contains the
same characters, but it is at a different location in memory.
The equals method tests equality, which Java strings define to mean
“containing the same characters.” Thus, string s3.equals the others.
public void testEquals() {
String s1 = "Java rocks!";
String s2 = s1;
String s3 = new String("Java rocks!");
assertTrue(s1 == s2);
assertFalse(s1 == s3);
assertTrue(s1.equals(s3));
}

Ruby also distinguishes between identity and equality. Each unique
object has an object_id. Two objects are identical if they have the same
object_id, and the equal? method tests for this:
irb(main):001:0> s1 = s2 = "Ruby rocks!"
=> "Ruby rocks!"
irb(main):002:0> s1.object_id
=> 190400
irb(main):003:0> s2.object_id
=> 190400
irb(main):004:0> s2.equal? s1
=> true

irb(main):006:0> s3 = "Ruby rocks!"
=> "Ruby rocks!"
irb(main):007:0> s4 = "Ruby rocks!"
=> "Ruby rocks!"
irb(main):008:0> s3==s4
=> true
irb(main):009:0> s3.eql? s4
=> true
irb(main):010:0> s3.equal? s4
=> false

Java’s == tests for identity, while Ruby’s ==
usually tests for equality



import java.util.*;
public class AccessMe {
private String name;
private List stuff;
public AccessMe(String name) {
this.name = name;
stuff = new ArrayList();
}
public String getName() {
return name;
}
protected List getStuff() {
return stuff;
}
private void clear() {
name = null;
stuff = null;
}
}

Ruby does not have any equivalent for package private but supports
public, protected, and private:
class AccessMe
def initialize(name)
@name = name
@stuff = []
end
attr_accessor :name
protected
attr_accessor :stuff
private
def clear
@name = @stuff = nil
end
end


Although access control specifiers set a general rule for how you can
use a class, the general rule may need to bend in some circumstances.
For example, an object serializer may bypass protection modifiers to
access all of an object’s state. In Ruby, you can bypass access control
specifiers with send:
a = AccessMe.new("test")
a.send :stuff=, 'some stuff'
puts a.send(:stuff)


You can invoke
the access control methods more than once for the same symbol. At first
glance this may seem silly—why would a class want to have different
levels of access control at different times? One possibility is temporarily
setting methods public so that unit tests can test them:
def test_clear
AccessMe.send :public, :clear, :stuff
a = AccessMe.new("test")
a.clear
assert_nil a.stuff
end

This sample uses only techniques we have covered thus far. You can
use cleaner ways to set methods public for the duration of a test. One
approach is to use the extend method, described in Section 3.8, Mixins,
on page 90.

try {
methodThatOpensFile();
} catch (FileNotFoundException fnfe) {
System.out.println("File not found " + fnfe);
} catch (Exception e) {
System.out.println("Caught " + e);
}

begin
File.read 'nonexistent'
rescue SystemCallError => e
puts 'system call failed'
rescue Exception => e
puts 'generic failure of some kind'
else
puts 'nothing failed'
ensure
puts 'this always executes'
end

begin
1/0
rescue Exception => e
puts "Message " + e.message
puts "Backtrace " + e.backtrace.join("\n")
end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值