文章目錄
原文鏈接
http://commons.apache.org/proper/commons-ognl/language-guide.html
前言
對象導航圖語言 (Object Graph Navigation Language),簡稱OGNL。
本人才疏學淺,對原文理解錯誤、翻譯錯亂之處,還請多多指出~
譯文
1 語法
基本的OGNL表達式非常簡單。雖然該語言的特性已經非常豐富,但是你通常不用擔心該語言更爲複雜的部分,當然,對於簡單的情形,就更不必擔心了。比方說,要從當前對象中獲取它的name屬性,對應的OGNL表達式就是一個簡單的name
。而如果要從headline
對象中獲取text
屬性,則對應的OGNL表達式是headline.text
。
什麼是屬性?大體來說,一個OGNL屬性和一個bean對象的屬性相同。意思是說一個屬性有一對兒get/set方法,或者就只是一個字段(詳細說起來比較複雜,因爲不同的對象有不同的屬性,後面我們細說)。
OGNL表達式的基本組成元素就是導航鏈,通常簡稱爲鏈。最簡單的鏈由以下部分組成:
表達式的組成元素 | 例子 |
---|---|
屬性名 | 比如上面提到的name 和headline.text |
方法調用 | 使用hashCode() 來獲取一個對象的哈希值 |
數組下標 | 使用listeners[0] 來獲取當前listeners數組的第一個元素 |
所有OGNL表達式都從當前對象的上下文中取值,導航鏈只使用上一個鏈接的結果做爲下一個鏈接的當前對象。你可以任意拓展鏈條的長度,如下面的例子所示:
name.toCharArray()[0].numericValue.toString()
上面表達式的取值步驟是:
- 用戶通過OGNL上下文,提供給OGNL的初始對象(或者叫根對象),從中可以獲取
name
屬性; - 對上一步的結果,調用
String
類的toCharArray()
方法; - 從上一步得到的數組中,取出第一個字符(因爲下標是0);
- 從上一步得到的字符中,獲取它的
numericValue
屬性(因爲這個字符是用Character
類表示的,此類有一個getNumericValue()
方法,可以獲取此屬性); - 上一步的結果是一個
Integer
對象,本步使用toString()
方法,最終把它轉換爲String
對象。
注意,上面的例子只能用來從一個對象中取值,而不能用來設置值。把上面的表達式傳遞給Ognl.setValue()
方法,將會拋出InappropriateExpressionException
(不合適的表達異常),這是因爲此鏈的最後一個鏈接,既不是屬性名,也不是數組下標,卻是一個toString()
方法。
上面這些語法足以完成你要做的絕大多數工作。
2 表達式
本節詳述OGNL表達式的各種元素。
2.1 常量
OGNL有下面幾種常量(也可以說是字面量):
- 字符串字面量,在Java中使用
" "
包圍,包圍在其中的"
和'
需要使用轉義,如\"
和\'
; - 字符字面量,在Java中使用
' '
包圍,同樣也可能會用到轉義; - 數值字面量,比Java多一些種類。除了Java的int, long, float和double類型外,OGNL還允許你使用
b
或B
後綴來表示BigDecimal
類型的數值,也允許使用h
或H
後綴來表示BigInteger
類型的數值。注意這裏的h
可以認爲是取自huge
,而不是取自hexadecimal
(16進制數字); - 布爾字面量,包含
true
和false
; null
字面量
2.2 引用屬性
OGNL在處理屬性引用時,針對不同的對象類型,採取不同的方式。
比如,從Map集合對象中引用一個任意類型的屬性(字符串類型 或 數值類型 等),就會以此屬性名爲key,返回此key對應的value。
再比如,從List集合對象或數組對象中引用一個數值類型的屬性時,就會以此屬性爲下標,返回對應的值。而從List集合對象或數組對象中引用一個字符串類型的屬性時,就調用此屬性的get/set方法等(和普通對象一樣)。
對於其它普通對象,它們只能處理字符串類型屬性的引用,調用此屬性的get/set(或者is/set)方法,或者直接調用這個字段(需要public訪問權限)。
注意這裏的新術語。屬性names
可以是任何類型,而不僅僅是字符串。但是要引用非字符串屬性,必須使用我們所說的 “索引” 符號。例如,要獲取數組的長度,可以使用以下表達式:
array.length
但是要獲取數組中下標爲0的元素,你就必需使用下面這樣的表達式:
array[0]
注意Java集合類有一些與之相關的特殊屬性。
2.3 索引
如上所述,雖然 “索引” 符號 是屬性引用中的計算形式,但是效果等同於常量形式。
比如,OGNL內部處理array.lenth
表達式的方式和下面的表達式的一樣:
array["length"]
/*
備註:array.length 就是所謂的常量形式
array["length"] 就是所謂的計算形式
這裏感覺有些牽強 😂
*/
而且,下面的表達式會得到相同的結果:
array["len"+"gth"]
2.3.1 數組 和 List集合的索引
針對Java的數組和List集合的索引非常簡單,就和Java本身的一樣。給一個整數類型的下標,就可以獲取到對應的元素。如果下標越界,就會拋出IndexOutOfBoundsException
異常,這和Java一樣。
2.3.2 JavaBeans的索引屬性
JavaBeans支持索引屬性的概念。具體來說,這意味着一個對象有一組遵循以下模式的方法:
public PropertyType[] getPropertyName()
;public void setPropertyName(PropertyType[] anArray)
;public PropertyType getPropertyName(int index)
;public void setPropertyName(int index, PropertyType value)
;
OGNL可以理解這些,並且通過索引符號提供對屬性的直接訪問。例如:
someProperty[2]
上面的例子會自動調用正確的索引屬性訪問器。在上例中,這個索引屬性訪問器就是getSomeProperty(2)
或setSomeProperty(2, value)
。如果沒有索引屬性訪問器,則會找到名爲someProperty
的屬性,並對其應用索引。
2.3.3 OGNL的對象索引屬性
OGNL拓展了索引屬性的概念,使得索引屬性可以使用任何類型的對象,而不僅僅是整數。在2.3.2中JavaBeans索引屬性只能是整數。當使用對象索引屬性時,OGNL會尋找符合下面模式的方法:
public PropertyType getPropertyName(IndexType index)
;public void setPropertyName(IndexType index, PropertyType value)
;
在get和set方法中,PropertyType
和IndexType
要一致。使用對象索引屬性的活生生的例子,就是Servlet API —— Session
類中attribute
屬性的get/set方法,如下所示:
public Object getAttribute(String name)
public void setAttribute(String name, Object value)
下面的表達式可以獲取或設置attribute
屬性:
session.attribute["foo"]
3 方法調用
因爲OGNL是解釋執行的,它必須在運行時選擇正確的方法,另外,除了提供的實際參數外,沒有關於此參數的、額外的類型信息,所以OGNL的方法調用和Java的不太一樣。OGNL總是選擇它能找到的與所提供的參數類型相匹配的最合適的方法。如果有兩個或以上與給定參數類型完全匹配的方法,OGNL則會隨意選擇一個來執行。
特別地,null
參數可以和任何非基本類型(也就是Java中的引用數據類型)相匹配,所以這比較容易導致方法調用錯誤。
注意,方法的參數是用逗號分隔的。因此不能在參數列表中使用逗號來做其它用途,除非在圓括號中使用它。比如:
method( ensureLoaded(), name )
會調用有2個參數的方法,然而:
method( (ensureLoaded(), name) )
則會調用有1個參數的方法。
4 變量引用
OGNL有一個簡單的變量模式,它允許你存儲中間結果並再次使用它們,或者只是給一個表達式重新命名,以使它更容易理解。OGNL的所有變量都是整個表達式的全局變量。你可以在變量名前使用#
符號來引用它,如下所示:
#var
OGNL也會把表達式計算過程中,各個節點的當前對象存儲在this
變量中,它可以像其它變量一樣被引用。比如,下面的表達式操作着listeners
集合的元素個數,如果它大於100,就把它乘以2,否則就給它加20。
listeners.size().(#this > 100? 2*#this : 20+#this)
OGNL可以被一個定義了變量初始值的映射調用。調用OGNL的標準方法,定義了root
變量(它保存着初始或根對象)以及context
變量(它存儲着變量所在的Map集合)。
要顯式地給一個變量賦值,只需在左邊寫一個變量引用賦值語句:
#var = 99
5 括號表達式
如你所料,括號表達式是一個計算單元,與周圍的操作符分開計算。使用括號表達式可以改變計算順序。括號表達式也是在方法調用中使用逗號操作符的唯一方式。
6 鏈接子表達式
如果你在點號後面使用括號表達式,那麼當前對象會貫穿整個括號表達式的執行過程。如下面的例子:
headline.parent.(ensureLoaded(), name)
整個計算過程是:先經過headline
和parent
屬性,然後調用ensureLoaded()
方法確保parent
對象已經加載,接着返回(或設置)parent
的name
屬性。
頂層表達式也可以使用這種方式鏈接。表達式的結果是它最右邊的元素。
ensureLoaded(), name
這會首先在根對象中調用ensureLoaded()
方法,然後從根對象中獲取name
屬性,做爲表達式的返回值。
7 構建集合
7.1 List集合
可以使用花括號括起來的一系列表達式來創建一個List集合。和方法調用一樣,這些表達式不能使用逗號操作符,除非被圓括號括起來。比如下面的例子:
name in { null,"Untitled" }
上面的例子會測試name
屬性是不是null
或者等於Untitled
。
上面描述的語法會創建一個List接口的實例,但具體的子類型沒有指定。
7.2 本地數組類型
有時你想創建Java本地的數組類型,比如int[]
或Integer[]
。OGNL支持用通常的方式創建本地數組,支持在創建時初始化或者只給出數組的長度。
new int[] { 1, 2, 3 }
上面的例子創建了一個int
類型的數組,包含3個元素:1,2,3。
可以給出數組的長度來創建一個全部是null
或0
的數組。
new int[5]
上面創建了一個int
數組,長度爲5,全部的元素都初始爲0。
7.3 Map集合
Map集合也可以使用特殊的語法來創建:
#{ "foo" : "foo value", "bar" : "bar value" }
這會創建一個Map集合,有2個key,分別是"foo"
和"bar"
。
想要指明具體的Map類型的人士,可以在花括號前指明,如下所示:
#@java.util.LinkedHashMap@{ "foo" : "foo value", "bar" : "bar value" }
上面的例子會創建一個LinkedHashMap
類型的實例,它可以保證有序。
8 對集合使用投影
OGNL提供了一種簡單的方式來從集合中提取出每個元素中共有的屬性或方法,我們把這種方法叫做“投影”,實際上“投影”是數據庫的術語,意思是從表中選擇列的子集。比如下面的例子:
listeners.{delegate}
這會返回所有listeners的delegate屬性。有關OGNL如何將各種對象做爲集合處理的,請查看“強制”這一節。
在投影過程中#this
變量表示集合迭代的當前元素。
objects.{ #this instanceof String ? #this : #this.toString()}
上面會創建一個新的集合,它的元素都是objects
集合元素的字符串形式。
9 從集合中選擇
OGNL提供了一種簡單的方式來從集合中選擇一些元素,然後把結果保存到一個新的集合中。我們把這種方法叫做“選擇”,實際上“選擇”是數據庫的術語,意思是從表中選擇行的子集。比如下面的例子:
listeners.{? #this instanceof ActionListener}
這會返回listeners集合中類型爲ActionListener
的元素的集合。
9.1 選擇第一個匹配的
爲了獲取匹配到的集合中第一個匹配的元素,你可以使用類似listeners.{? true}[0]
的索引來實現。然而,這種方式比較笨,因爲如果匹配沒有結果(也就是返回的集合是空集),那麼你就會收到ArrayIndexOutOfBoundsException
異常。
選擇的語法也支持選擇第一個匹配的元素,並以一個集合的形式返回。如果所有元素的都匹配沒有成功,那麼就會返回一個空集合。
objects.{^ #this instanceof String }
上面的例子會返回objects
集合的第一個String
類型的元素。
9.2 選擇最後一個匹配的
與獲取第一個匹配的類似,有時你會想獲得最後一個匹配的元素。
objects.{$ #this instanceof String }
這會返回objects
集合的最後一個String
類型的元素。
10 調用構造方法
你可以在Java中,用new
操作符創建一個對象,OGNL也可以這樣做。但有一個不同是在OGNL中,你必須使用類的全限定名,除非你使用的類在java.lang
包中(比如,應該使用new java.util.ArrayList()
,而不是new ArrayList()
)。
只有使用默認的類解析器時纔會出現上面這種情況。通過自定義類解析器,可以抹除類全限定名的限制。具體可以參考OGNL開發者文檔中有關ClassResolver
類的介紹。
OGNL會選擇合適的構造方法,其原理就和在重載的方法中找到合適的一樣。
11 調用靜態方法
你可以使用@class@method(args)
的語法,來調用靜態方法。如果不寫class
,那麼默認的就是java.lang.Math
,爲的是讓調用min
和max
等方法更方便一些。如果你要寫class
,那麼請使用類的全限定名。
如果你想調用類中的靜態方法,也可以通過此類的對象來調用,就好像這個靜態方法是一個實例方法一樣。
如果這個靜態方法被重載了,OGNL會選擇合適的靜態方法,其原理就和在普通重載的方法中找到合適的一樣。
12 調用靜態變量
你可以使用@class@field
的語法,來調用靜態變量。這個類必須全限定名。
13 表達式取值
如果有一個OGNL表達式,記做A,A的後面有一個括號表達式B,而且B前面沒有點,那麼OGNL會把A的結果做爲新的表達式,記爲C,然後把B的結果做爲根對象D,由D調用表達式C。直觀一點表示就是:
A(B)
A的結果是C
(B)的結果是D
最終執行D.C
/*
這裏 彎彎繞得厲害,原文真難懂 😂
*/
表達式A的結果可以是任意對象(記爲C),如果C是一個AST(抽象語法樹),那麼OGNL就假設C是一個表達式的解析形式,然後直接解解釋C。否則,它就獲取C的字符串值(記爲E),把E解析爲AST後,再解釋它。
例如:
#fact(30H)
上例會查找fact
變量,把fact
變量的值解釋爲OGNL表達式,並被類型爲BigInteger
,值爲30的對象調用。看後面 僞Lambda表達式 的例子可以明白,上例最終會計算出30的階乘。需要注意的是,表達式求值和方法調用在語法上有歧義。OGNL處理的方式是能當方法調用,就當方法調用。比如說,當前對象有一個fact
屬性,它存儲着OGNL求階乘的表達式,你不能用下面的方式調用它來做表達式求值:
fact(30H)
因爲OGNL會把它當做fact
方法調用,而不是當做表達式求值。解決辦法是,你可以用圓括號把fact
屬性括起來,用以表示是要做表達式求值:
(fact)(30H)
14 僞Lambda表達式
OGNL提供了簡潔的Lambda表達式語法。Lambda表達式可以讓你寫一些簡單的函數。但它不是真正意義上的Lambda表達式,因爲這裏沒有閉包——在OGNL中,所有的變量都是全局的。
比如,下例的OGNL表達式聲明瞭一個遞歸實現求階乘的函數,然後調用此函數:
#fact = :[#this<=1? 1 : #this*#fact(#this-1)], #fact(30H)
方括號中的就是Lambda表達式。#this
變量是函數的參數,它的初始值是30H
,然後在每次的遞歸調用時都遞減1。
OGNL把Lambda表達式當做常量。Lambda表達式的值是AST(抽象語法樹),AST就是表達式的解析形式。
15 集合的僞屬性
OGNL爲集合提供了一些特殊的屬性。這樣做的原因是集合在方法命名方面不遵循JavaBeans的模式,因此需要調用size(), length()等方法,而不能直觀地將它們當屬性調用。OGNL改變了這一現狀,它提供了一些內置的僞屬性。
集合的特殊僞屬性
集合 | 特殊的屬性 |
---|---|
Collection (被Map , List , Set 繼承) |
♦ size :集合的長度;♦ isEmpty :判斷集合是否爲空 |
List |
♦ iterator :獲取List 的迭代器Iterator |
Map |
♦ keys :獲取Map 中所有key的Set 集合♦ values :獲取Map 中所有value的Collection 集合注意 這些屬性,加上 size 和isEmpty ,不同於map集合 索引形式的數據訪問方式(例如,someMap["size"] 會從集合中獲取key等於"size" 對應的值,然而someMap.size 會獲取map集合的長度) |
Set |
♦ iterator :獲取Set 的迭代器Iterator |
Iterator |
♦ next :獲取迭代的下一個元素♦ hasNext :判斷迭代過程是否還有下一個元素 |
Enumeration |
♦ next :獲取枚舉迭代的下一個元素♦ hasNext :判斷枚舉迭代是否還有下一個元素♦ nextElement :next 的同義詞♦ hasMoreElements :hasNext 的同義詞 |
16 一些與Java不同的操作符
大多數OGNL的操作符都借鑑自Java,其效果與Java的也一樣。可以看OGNL語法參考來獲取更多信息。下面我們要說的是Java裏沒有,而OGNL有的操作符,以及與Java不同的OGNL操作符。
- 逗號操作符(也叫順序操作符)。這借鑑自C語言。逗號用於分隔兩個完全獨立的表達式。逗號表達式中第2個表達式的值,就是整個逗號表達式的返回值。例如:
ensureLoaded(), name
當這個表達式被計算時,首先會調用ensureLoaded()
方法(此方法的含義很可能是要確保當前對象的所有組成部分都已加載到內存),然後會取出name
屬性的值(如果是取值操作的話)或者替換name
屬性的值(如果是設值操作的話);
- 你可以使用一對兒花括號聲明一個List集合對象,如下:
{ null, true, false }
in
操作符(not in
是它的反義詞)。這倆是用來做包含判斷的,看一個值是否在集合中。比如:
name in {null,"Untitled"} || name
17 設值 vs 取值
如前所述,一些值是可獲取的,但不可設置。這是表達式本身的問題。比如:
names[0].location
就是一個可以設置的表達式——這是由表達式的尾部是一個可設置的屬性決定的。
然而,有一些表達式,比如:
names[0].length + 1
是不可設置的,因爲表達式的尾部不能解析爲一個對象的屬性。它只是一個計算值。如果你想用Ognl.setValue()
方法來計算此表達式,就會得到InappropriateExpressionException
異常。
也可以使用=
和取值表達式來設置變量。這種做法在取值表達式需要設置一個變量作爲執行副本的時候就會很有用。
18 類型的強制轉換
接下來,我們介紹OGNL如何把對象解析爲多種類型。看下面的例子,就可以明白OGNL如何把對象轉換爲boolean
, number
, integer
和collection
。
18.1 把對象解析爲布爾類型 Booleans
在OGNL中,任何對象都可以當布爾類型來用。OGNL解析把對象解析爲布爾類型的規則是:
- 如果這個對象是
Boolean
類型,就直接返回; - 如果這個對象是
Number
類型,則將其雙精度浮點值與0進行比較。非0爲true
,0是false
; - 如果這個對象是
Character
類型,它的字符值不是0的時候返回true
,否則返回fasle
; - 其他的情況,只有對象不爲
null
時返回true
,否則返回false
。
18.2 把對象解析爲數值類型 Numbers
數學運算符會試圖將操作數視爲數字。基本數據類型的包裝類(Integer
, Double
以及Character
和Boolean
等,都會被視爲整數)和java.math
包下的“大”數值類型(BigInteger
和BigDecimal
),它們被認爲是特殊的數字類型。對於其他類型的對象,OGNL會嘗試把這些對象的字符串值轉換爲數值。
對於有兩個操作數的數學運算符,可以使用下面的規則來確定計算結果的類型。實際運算結果的類型可能要比下面給出的要寬:
- 如果兩個操作數是相同的類型,那麼結果也是相同的類型(如果有返回結果的話);
- 如果某個操作數不是數值類型,那麼它會被當成
Double
來使用; - 如果兩個操作數都是實數的近似值(
Float
,Double
或BigDecimal
類型),那麼結果的類型將是兩個操作數的類型中更寬的那個; - 如果兩個操作數都是整數類型(
Boolean
,Byte
,Character
,Short
,Integer
,Long
或者BigInteger
類型),那麼結果的類型將是兩個操作數的類型中更寬的那個; - 如果一個操作數是實數類型,而另一個是整數類型,那麼若是這個整數的取值小於
int
的範圍,結果的類型就是實數類型。若是這個整數的類型是BigInteger
,那麼結果的類型就是BigDecimal
。其他的情況,結果的類型就是比那個實數類型的操作數更寬的類型,或者Double
類型。
18.3 把對象解析爲整數 Integers
對於只作用於整數的操作符(比如位移運算符),會把它們的操作數當做數值。這數值的類型,除了原BigDecimal
和BigInteger
類型會被當做BigInteger
來處理外,其他的數值類型都會被當做Long
。
對於操作數被當做BigInteger
的情況,返回值仍然是BigInteger
類型;對於操作數被當做Long
的情況,返回值可以是與操作數一樣的類型,或者合適的類型,若都不滿足,則最終是Long
類型。
18.4 把對象解析爲集合 Collections
集合的投影和選擇操作符(例如:e1.{e2}
和e1.{?e2}
),以及in
操作符,都將其中一個操作數當做集合來遍歷。針對不同類型的操作數,會有不同的情況:
- Java數組從前到後遍歷;
java.util.Collection
接口的成員,使用迭代器來遍歷;java.util.Map
接口的成員,使用迭代器來遍歷集合的value;java.util.Iterator
和java.util.Enumeration
的元素通過迭代來遍歷;java.lang.Number
的對象“遍歷”時,會返回從0開始,到小於此數值的整數構成的一系列整數;- 所有其它對象,都會被視爲只包含它們自己的,只有一個長度的集合。
19 附錄:OGNL 語法參考
本節將非常詳細地述說OGNL的語法和實現。請參閱下面的OGNL完整操作符表、OGNL如何進行類型強制轉換 和 OGNL基本表達式詳述。
19.1 操作符
OGNL借鑑了大多數Java操作符,並添加了一些新操作符。在大多數情況下,OGNL處理給定操作符的方式與Java相同,但需要注意的是,OGNL本質上是一種無類型的語言。這意味着OGNL中的每個值都是一個Java對象,OGNL會把每個對象強制轉換爲適合其使用情況的類型(請參閱18 類型強制轉換 一節)。
下表OGNL操作符的順序,是按優先級由低到高排列的。當同一個單元格中有多個操作符時,表示這些操作符具有相同的優先級,在表達式中同時出現時,會按從左到右的順序計算。
操作符 | 整個表達式被取值時 | 整個表達式被設值時 |
---|---|---|
e1, e2 順序操作符(也叫逗號表達式) |
e1 和e2 使用相同的源對象進行計算,並返回e2 的計算結果 |
會對e1 進行取值,然後對e2 進行設值 |
e1 = e2 賦值操作符 |
會對e2 進行取值,對e1 進行設值(設置的就是e2 的值) |
不能對整個表達式設值 |
e1 ? e2 : e3 三目操作符 |
對e1 進行取值,並把它轉化爲布爾類型,然後依據它爲true 或false 來對e2 或e3 進行取值 |
對e1 進行取值,對e2 或e3 進行設值 |
e1 || e2 或者寫成 e1 or e2 邏輯或操作符 |
對e1 取值,如果值爲true ,就返回true ,否則才取e2 的值。簡單地說,就是短路或 |
當e1 的值爲false 時,對e2 設值。否則不進行設置 |
e1 && e2 或者寫成e1 and e2 邏輯與操作符 |
對e1 取值,如果值爲false ,就直接返回false 。否則才取e2 的值。簡單地說,就是短路與 |
當e1 的值爲true 時,對e2 設值。否則不進行設值 |
e1 | e2 或者寫成e1 bor e2 按位或操作符 |
e1 和e2 會解析換爲整數,計算結果也是一個整數 |
不能對整個表達式設值 |
e1 ^ e2 或者寫成e1 xor e2 按位異或操作符 |
e1 和e2 會被解析爲整數,計算結果也是一個整數 |
不能對整個表達式設值 |
e1 & e2 或者寫成e1 band e2 按位與操作符 |
e1 和e2 會被解析爲整數,計算結果也是一個整數 |
不能對整個表達式設值 |
e1 == e2 或者寫成e1 eq e2 等於操作符 e1 != e2 或者寫成e1 neq e2 不等於操作符 |
把它當成Java中的equals() 方法就好 |
不能對整個表達式設值 |
♦ e1 < e2 或者寫成e1 lt e2 ♦ e1 <= e2 或者寫成e1 lte e2 ♦ e1 > e2 或者寫成e1 gt e2 ♦ e1 >= e2 或者寫成e1 gte e2 ♦ e1 in e2 ♦ e1 not in e2 |
如果這些操作符的操作數不是數值類型,而且實現了Comparable 接口,那麼就使用compareTo() 方法來比較。其它情況,OGNL就會把操作數解析爲數值,進行數值比較。in 操作符不是Java自帶的,它會判斷集合e2中是否包含元素e1。in 操作符效率不高,它會遍歷集合,並使用標準的OGNL等值判斷 |
不能對整個表達式設值 |
♦ e1 << e2 或者寫成e1 shl e2 按位左移操作符 ♦ e1 >> e2 或者寫成e1 shr e2 按位右移操作符 ♦ e1 >>> e2 或者寫成e1 ushr e2 邏輯右移操作符 |
e1 和e2 會被解析爲整數,計算結果也是一個整數 |
不能對整個表達式設值 |
♦ e1 + e2 ♦ e1 - e2 |
相加操作符會做字符串拼接,或者數值相加;相減操作符只能作用於數值 | 不能對整個表達式設值 |
♦ e1 * e2 ♦ e1 / e2 ♦ e1 % e2 |
乘、除會把操作數解析爲數值類型,取餘會把它的操作數解析爲整數類型 | 不能對整個表達式設值 |
♦ + e 一元加操作符 ♦ - e 一元減操作符 ♦ ! e 或者not e 邏輯非 ♦ ~ e 按位非 ♦ e instanceof class 判斷是否爲子類 |
一元加操作符,如果操作數是數值類型,那麼就返回它本身;如果操作數是字符串類型,那麼就把它轉換爲數值。 一元減操作符,如果操作數是數值類型,那麼就對它求負;如果操作數是字符串類型,那麼就把它轉換爲數值,並求負。 按位非不會把操作數解析爲整數。 |
不能對整個表達式設值 |
♦ e.method(args) 方法調用♦ e.property 取屬性♦ e1[e2] 索引♦ e1.{e2} 投影♦ e1.{? e2} 選擇♦ e1.(e2) 鏈接子表達式♦ e1(e2) 表達式求值 |
總的來說,導航鏈從左到右一個一個地計算 | 這些操作符中,只有一部分可以對整體設值。只有鏈尾是屬性調用(e.property ),或者是索引(e1[e2] ),再或者是子表達式(e1.(e2) ),另外取值表達式也可以。 |
♦ constant 常量( e ) 逗號表達式♦ method(args) 方法調用♦ property 屬性調用♦ [ e ] 索引♦ { e, ... } 創建List集合♦ #variable 上下文變量引用♦ @class@method(args) 靜態方法調用♦ @class@field 靜態字段調用♦ new class(args) 構造方法調用♦ new array-component-class[]{ e, ... } 創建數組♦ #{ e1 : e2, ...} 創建Map集合♦ #@classname@{ e1 : e2, ...} 使用具體的Map類型創建集合♦ :[ e ] Lambda表達式 |
基本表達式 | 只有屬性調用,索引調用或者 上下文變量調用 可以對整體設值。 其中,索引調用 [ e ] ,會先計算e 的值,然後做爲當前對象要設值的屬性名(可以是字符串類型,或者其他任何類型)。剩下的 上下文變量 和 屬性調用的設值就更直接了。 |