程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java.next:第一部分——共同點

Java.next:第一部分——共同點

編輯:關於JAVA

本文是Java.next系列的第一部分。在這一部分,我將探討作為Java.next的語言所具有的共同特征。

我選擇了四種語言作為“Java.next”的代表:Clojure,Groovy,JRuby,以及Scala。乍看起來,這幾種語言有著 很大的不同。Clojure是Lisp方言;Groovy是作為“類Java”的選擇;JRuby即具有Ruby語言的優雅,同時也有著Rails所帶來 的優勢;與其他都不一樣的是Scala,它有著靜態語言所具有的特點。

正如你所料想的一樣,有很多關於這些語言中誰才是最好的辯論。之所以有著這麼多的辯論,很大程度上是因為這些語言有著很多共同點。它們有著一個共同的演變背景:Java語言。Java語言所具有的優點以及缺陷影響著這些語言的設計方向。

在這篇文章中,我著重從下面兩個方面來闡述這些語言的共同點:

☆ 過去的10年中,我們在基於虛擬機、面向對象的語言編程中得到了很多關於如何開發易讀的、可維護的應用。Java.next吸取了這些成果,使得這些語言更注重於問題的本質而不是形式。

☆ “本質 VS 形式”的設計理念使得編程方式發生了很大的改變,這種觀念的變化比以前從C/C++到Java的轉變更大。

我將Java.next所具有的共同優點概括為以下八點:

● 一切皆對象

● 簡潔的屬性定義方式

● 易用的集合類

● 函數式編程

● 運算符重載

● 可維護的異常處理

● 給已有類增加新方法

● 創建新的語言結構

一切皆對象

在Java中,我們每時每刻都要面對對象類型與基本類型的不同之處。這種不同導致三個實際問題:

1.API必須寫兩份:一個針對對象類型;一個針對基本類型。更糟糕的情形是需要重寫多份:一個針對對象類型,然後對每一個基本類型各寫一份。

2.默認的數值類型有著范圍限制,一旦越界,程序會以詭異的形式中斷。

3.對於那些高精度類型(譯者注:指BigInteger等類型),你不能使用直觀的數學操作符(+,-,etc.)來操作它們。

在Java.next中,一切皆是對象。你可以在所有的類型上使用相同的語法調用方法。

; clojure
(. 1 floatValue)
1.0
// groovy
1.floatValue()
===> 1.0
# ruby
1.to_f
=> 1.0
// scala
1.floatValue
res1: Float = 1.0

簡潔的屬性定義方式

在Java中建立一個屬性,你必須定義一個域,一個Getter,一個Setter,以及(通常)一個相應的構造函數,每一個定義都需要適當的訪問修飾詞。在Java.next中,你可以畢其功於一役。

; clojure
(defstruct person :first-name :last-name)
// groovy
class Person {
   def firstName
   def lastName
}
# ruby
Person = Struct.new(:first_name, :last_name)
// scala
case class Person(firstName: String, lastName: String)

如果你需要復寫(或刪除)一個Getter,Setter或是構造函數,你也可以做到,而無需重寫其它部分。

這還不是全部。所有這些語言信奉TMTOWTDI (There's More Than One Way To Do It),因此這兒不止一種方式可以實現上面同樣的要求。

易用的集合類

Java.next針對大部分重要的集合類提供了便利的語法:array與map。此外,你可以通把將函數作為參數將一系列操作連貫起來,而避免使用Java中那種顯式的迭代或循環方式。譬如:找出100以內的奇完全平方數:

; clojure
(filter (fn [x] (= 1 (rem x 2))) (map (fn [x] (* x x)) (range 10)))
(1 9 25 49 81)
// groovy
(1..10).collect{ it*it }.findAll { it%2 == 1}
===> [1, 9, 25, 49, 81]
# ruby
(1..10).collect{ |x| x*x }.select{ |x| x%2 == 1}
=> [1, 9, 25, 49, 81]
// scala
(1 to 10).map(x => x*x).filter(x => x%2 == 1)
res20: Seq.Projection[Int] = RangeMF(1, 9, 25, 49, 81)

對哈希表(字典)有著同樣便利的操作。

函數式編程

上面集合類的便利使用只是函數式編程的一個特殊情形。Java.next中函數作為一等公民,支持將函數作為參數,將函數作為返回值,以及支持閉包。舉一個簡單的例子:創建一個函數adder,使得能計算與一個運行時指定的值之和:

; clojure
(defn adder [x] (fn [y] (+ x y)))
// groovy
adder = { add -> { val -> val + add } }
# ruby
def adder(add)
  lambda { |x| x + add }
end
// scala
def sum(a: Int)(b: Int) = a + b

運算符重載

在Java中,你不能重載運算符。因此你只能像這樣操作BigDecimal:

// Java math
balance.add(balance.multiply(interest));

Java.next允許你重載運算符。這樣你能夠創建新的類型並使得它像內建類型一樣工作。譬如你能夠新建一個ComplexNumber或RationalNumber,使其支持+,-,*,/運算符:

; Clojure
(+ balance (* balance interest))
// Groovy
balance + (balance * interest)
# JRuby
balance + (balance * interest)
// Scala
balance + (balance * interest)

可維護的異常處理

檢查型異常(Checked Exception)是一個失敗的試驗。檢查型異常的處理代碼使得Java代碼變得臃腫並掩蓋了問題的關鍵。更糟糕的是,檢查型異常使得代碼的維護變得困難。

Java.next不要求你聲明檢查型異常,也不要求你顯式的處理檢查型異常。這表明Java平台的其他語言完全可以避免Java語言中丑陋的檢查型異常。

給已有類增加新方法

在Java中,你不能給已有類添加方法。這使得面向對象模型變得很荒謬,開發者需要創建一些工具類,而這是與OO相違背的。

// Java (from the Jakarta Commons)
public class StringUtils {
  public static boolean isBlank(String str) {
   int strLen;
   if (str == null || (strLen = str.length()) == 0) {
    return true;
   } 
   for (int i = 0; i < strLen; i++) {
   if ((Character.isWhitespace(str.charAt(i)) == false)) {
    return false;
   }
  }
}

在Java.next中,你能夠給已有類增加方法:

; Clojure
(defmulti blank? class)
(defmethod blank? String [s] (every? #{\space} s))
(defmethod blank? nil [_] true)
// Groovy
String.metaClass.isBlank = {
  length() == 0 || every { Character.isWhitespace(it.charAt(0)) }
}
# Ruby (from Rails)
class String
  def blank?
   empty? || strip.empty?
  end
end
// Scala
implicit def strWrapper(s :String) = new {
   def isBlank = s.forall{ _.isWhitespace }
}

創建新的語言結構

Java包括Java語言以及API庫,這兩部分是截然不同的:你能夠創建新的API庫,但你不能夠添加新的語言特性。

而在Java.next中,API庫與語言特性沒有明顯的界線。你能夠創建新的語言結構使得它像核心語言特性一樣工作。例如,Clojure提供了一個and函數:

; clojure
(and 1 2) => 2

你需要解決的不一定都是這樣二元化的問題。你可能需要一個most函數,當它參數中大部分真時返回true。Clojure中沒有這個,但你可以自己動手寫一個:

; clojure
(most 1 2) => true
(most 1 2 nil) => true
(most 1 nil nil) => false

這裡的關鍵之處不是“我的語言是否需要一個most條件?”,而是不同的領域有不同的需求。在Java.next中,語言與庫的界限被最小化,你可以添加適當的語言特性以適應你的領域需求,而不是相反。

再舉一個例子,考慮Ruby的attribute語法:

# Ruby
class Account
  attr_accessor :name
  dsl_attribute :type
end

attr_accessor是Ruby固有的語法. dsl_attribute是我寫的一個庫方法,它允許你在做賦值操作的時候省略"=",像下面這樣:

# normal attributes
account.name = "foo"
# equals-free attributes
account.type checking

結論

這些Java.next語言有著相當多的共同點。盡管我使用一些孤立的例子來說明這些特點,但只有將它們一起使用時才能體現這些語言的真正威力。綜合Java.next的所有特征,會導致一個完全不同的編碼方式:

★ 在編碼時你不再需要為了代碼的可測試與適應性而采取保守方式:使用類工廠,設計模式以及依賴注入。作為代替,你可以構建一個最小解決方案,並隨時改進它。

★ 在Java.next中,你可以開發更適合你問題的內部領域特定語言(DSLs)。

以我的經驗,這種編碼方式能將代碼量減少一個數量級,同時提高代碼的可讀性。

很多人在尋找“next big language”。下一個“big language”已經在這裡,但它不是一個單獨的語言,而是一組概念的綜合體,就如Java.next中表現出來的那樣。

過渡到Java.next配的上"big"這個稱號嗎?絕對可以。根據我的經驗,一旦你作出轉變,每一步都有著巨大的進步,包括學習曲線以及生產力。

在這個系列的後續部分,我將討論這些語言的不同之處。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved