大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全家桶1年46,售后保障稳定
- Property expressions
- Boolean expressions
- Method invocations
- Variable assignments
- Function definitions
1:简单的属性表达式:
user.name == 'John Doe'
(user.name == 'John Doe') && ((x * 2) - 1) > 20
statement1; statement2; statement3
Unary Operators
Operator |
Description |
Example |
---|---|---|
new |
Object instantiation |
new String(“foo”) |
with |
Block WITH Operator. Perform multiple operations on a single object instance |
with (value) { name = ‘Foo’, age = 18, sex = Sex.FEMALE } |
assert |
Assert that a value is true or fail with an AssertionError |
assert foo != null |
isdef |
Tests whether or not a variable is defined within the scope |
isdef variableName |
! |
Boolean negation operator |
!true == false |
Comparison Operators
Operator |
Description |
Example |
---|---|---|
== |
Equality Check. Checks to see if the values on both sides of the operator are equal. Unlike Java, this is not an identity check. |
“foo” == “foo” is true |
!= |
Not Equals Check. Checks to see if the values on both sides of the operator are not equal. |
“foo” == “bar” is false |
> |
Greater Than Check. Checks to see if the value on the left side of the operator is greater than than value on the right. |
2 > 1 is true |
< |
Less Than Check. Checks to see if the value on the left side of the operator is less than value on the right. |
1 < 2 is true |
>= |
Greater Than or Equal. Checks to see if the value on the left hand side is greater than or equal to the value on the right. |
1 >= 1 is true |
<= |
Less Than or Equal. Checks to see if the value on the left hand side is less than or equal to the value on the right. |
2 <= 2 is true |
contains |
Value Containment Check. Checks to see if the value on the left contains the value on the right. For more details on how this operator works, see Contains Operator |
var contains “Foo” |
is orinstanceof |
Type Checking Operator. Checks to see if the value on the left is a member of the class on the right. |
var instanceof Integer |
strsim |
String Similarity Check. Compares to strings and returns a similarity between them as a percentage. See: String Similarity Check. |
“foobie” strsim “foobar” |
soundslike |
Soundex Check. Performs a soundex comparison between two strings. See: Soundex. |
“foobar” soundslike “fubar” |
Logical Operators
Operator |
Description |
Example |
---|---|---|
&& |
Logical AND. Checks to see that the values on both sides of the operator are true. |
foo && bar |
|| |
Logical OR. Checks to see if either the value on the left or the right is true. |
foo || bar |
or |
Chained OR. Checks a sequence of values for emptinessand returns the first non-empty value. (This operator, or at least it’s equivalent functionality, is referred to as the “elvis operator” in other languages) |
foo or bar or barfoo or ‘N/A’ |
~= |
Regular Expression Match. Checks to see if the value on the left matches the regular expression on the right. |
foo ~= ‘[a-z].+’ |
Bitwise Operators
Operator |
Description |
Example |
---|---|---|
& |
Bitwise AND. |
foo & 5 |
| |
Bitwise OR. |
foo | 5 |
^ |
Bitwise XOR. |
foo ^ 5 |
Arithmetic Operators
Operator |
Description |
Example |
---|---|---|
+ |
Addition. Adds the value on the left to the value on the right |
1 + 2 |
– |
Subtraction. Subtracts the value on the right from the value on the left |
2 – 1 |
/ |
Division. Divides the number on the left by the number on the right |
2 / 1 |
* |
Multiplication. Multiples the number on the left by the number on the right |
1 * 2 |
% |
Modulus. Divides the number on the left by the number on the right and returns the remainder. |
2 % 1 |
Other Operators
Operator |
Description |
Example |
---|---|---|
+ |
String Concatenation. Overloaded operator for concatenating two strings together. |
“foo” + ;bar” |
# |
Concatenation Operator. Concatenates two literals as strings. |
1 # 2returns “12” |
in |
Projection/Fold. Projects across a collection. See:Projections and Folds. |
(foo in list) |
= |
Assignment. Assigns the value on the right to the variable on the left. |
var = “foobar” |
1:判断值是否为emptiness
MVEL提供了一个特殊的字符来表示值为emptiness的情况,叫作empty,如:foo == empty,若foo满足emptiness的任何条件,这个表达式值都为true
For example:
foo == empty
若foo满足emptiness的任何条件,这个表达式值都为true
foo == null; foo == nil; // same as null
"123" == 123;
['Bob' : new Person('Bob'), 'Michael' : new Person('Michael')]
Map map = new HashMap();
map.put("Bob", new Person("Bob"));
map.put("Michael", new Person("Michael"));
这是一个非常强大的方式来表达MVEL内的数据结构。你可以在任何地方使用这些构造,比如作为方法的参数:
something.someMethod(['foo' : 'bar']);
Lists
列表用下面的格式来描述: [item1, item2, …]
For example:
["Jim", "Bob", "Smith"]
Maps
Map用下面的格式来描述: [key1 : value1, key2: value2, …]
For example:
["Foo" : "Bar", "Bar" : "Foo"]
Arrays
数组用下面的格式来描述: {item1, item2, …}
For example:
{"Jim", "Bob", "Smith"}
foo.someMethod({1,2,3,4});
if (user.manager != null) { return user.manager.name; } else { return null; }
当Map的key是String类型时,还可以使用特殊的方式来访问,如:user.foobar,也就是允许你把map本身看成一个虚拟的对象,来访问其属性
字符串作数组
为了能使用属性的索引(迭代也是如此),所有的字符串都可以看成是一个数组,在MVEL中你可以用下面的方式来获取一个字符串变量的第一个字符:
A literal is used to represent a fixed-value in the source of a particular script.
String literals
在脚本语言中,一段文字用来代表一个固定的值
字符串常量:
字符串常量可以用一对单引号或一对双引号来界定。如:
"This is a string literal" 'This is also string literal'
字符串中的特殊字符
\\
– Double escape allows rendering of single backslash in string.\n
– Newline\r
– Return\u####
– Unicode character (Example:\uAE00
)\###
– Octal character (Example:\73
)
数字常量
整数可以表示为十进制(基数为10),8进制(基数为8),或十六进制(基数为16)。
一个十进制数字,不从零开始(相对于8进制、16进制而言),可以表示任意数,如:125
125 // decimal
一个八进制数,以0为前缀,后面跟着0到7内的数字
0353 // octal
一个十六进制,以0X为前缀,后面可以跟着0-9,A-F范围内的数字
0xAFF0 // hex
浮点常量
A floating point number consists of a whole number and a factional part denoted by the point/period character, with an optional type suffix.
10.503 // a double 94.92d // a double 14.5f // a float
二进制常量
You can represent BigInteger
and BigDecimal
literals by using the suffixes B and I(uppercase is mandatory).
104.39484B // BigDecimal 8.4I // BigInteger
布尔常量
Boolean literals are represented by the reserved keywords true and false.
Null 常量
The null literal is denoted by the reserved keywords null or nil.
if (var > 0) {
System.out.println("Greater than zero!");
}
else if (var == -1) {
System.out.println("Minus one!");
}
else {
System.out.println("Something else!");
}
var > 0 ? "Yes" : "No";
var > 0 ? "Yes" : (var == -1 ? "Minus One!" : "No")
count = 0;
foreach (name : people) {
count++;
System.out.println("Person #" + count + ":" + name);
}
System.out.println("Total people: " + count);
str = “ABCDEFGHIJKLMNOPQRSTUVWXYZ”;
foreach (el : str) { System.out.print("[" + el + "]"); }
[A][B][C][D][E][F][G][H][I][J][K][L][M][N][O][P][Q][R][S][T][U][V][W][X][Y][Z]
foreach (x : 9) { System.out.print(x); }
Do While, Do Until
和java中的一样,MVEL也实现了Do While,Do Until,While和Until意义正好相反。
do { x = something(); } while (x != null);
… is semantically equivalent to …
do { x = something(); } until (x == null);
While, Until
MVEL 2.0 implements standard while, with the addition of the inverse until.
while (isTrue()) { doSomething(); }
… or …
until (isFalse()) {
doSomething();
}
parentNames = (parent.name in users);
familyMembers = (name in (familyMembers in users));
Filters
(doSomeMethod() in listOfThings if $.shouldBeRun())
($ in fooList if $.name contains 'foobie')
(toUpperCase() in ["foo", "bar"]); // returns ["FOO", "BAR"]
(($ < 10) in [2,4,8,16,32]); // returns [true, true, true, false, false]
($ in [2,4,8,16,32] if $ < 10); // returns [2,4,8]
/** * Sample MVEL 2.0 Script * "Functional QuickSort" * by: Christopher Michael Brock, Inspired by: Dhanji Prasanna */ import java.util.*; // the main quicksort algorithm def quicksort(list) { if (list.size() <= 1) { list; } else { pivot = list[0]; concat(quicksort(($ in list if $ < pivot)), pivot, quicksort(($ in list if $ > pivot))); } } // define method to concatenate lists. def concat(list1, pivot, list2) { concatList = new ArrayList(list1); concatList.add(pivot); concatList.addAll(list2); concatList; } // create a list to sort list = [5,2,4,1,18,10,15,1,0]; // sort it! quicksort(list);
赋值
MVEL允许你对表达式中的变量进行赋值,以便在运行时获取,或在表达式内部使用。因为MVEL是动态类型语言,所以你不必为了声明一个变量
而指定其类型。当然,你也可以选择指定。
str = "My String"; // valid
String str = "My String"; // valid
与java语言不同的是,当给一个指定类型的变量赋值时,MVEL会提供自动的类型转换(可行的话),如:
String num = 1;
assert num instanceof String && num == "1";
对于动态类型变量而言,你要想对其进行类型转换,你只需要将值转换成相应的类型既可:
num = (String) 1;
assert num instanceof String && num == "1";
十、
MVEL可以使用def或function关键字来定义本地函数。
函数必须是先声明后引用,唯一例外的是递归调用的时候。
1、简单示例
定义函数:
def hello() { System.out.println("Hello!"); }
定义了一个没有参数的函数hello.当调用该函数时会在控制台打印"Hello!". An MVEL-defined function works just like any regular method call,
and resolution preference is to MVEL functions over base context methods.
hello(); // calls function
2、传参和返回值
函数可以接收参数和返回一个值,看下面的例子:
def addTwo(a, b) {
a + b;
}
这个函数会接收两个参数(a和b),然后将这两个变量相加。因为MVEL遵循last-value-out原则,所以
结果将会被返回。因此,你可以这样来使用这个函数:
val = addTwo(5, 2);
assert val == 10;
当然,也可以使用return 关键字来强迫从程序内部返回一个函数值。
3、closures
MVEL支持closure,虽然,其功能与本地java函数没有任何关联。
// define a function that accepts a parameter
def someFunction(f_ptr) { f_ptr(); }
// define a var
var a = 10;
// pass the function a closure
someFunction(def { a * 10 });
十一、Lambda表达式
MVEL允许定义Lambda方法,如下所示:
threshold = def (x) { x >= 10 ? x : 0 };
result = cost + threshold(lowerBound);
上面的例子定义了一个Lambda,并将其赋值给变量"threshold".Lambda实质上就是一个用来给变量赋值的函数,也是closure
十二、宏
MVEL支持通过宏定义来用外部的可扩展资源来代替一个标记,这是它的一项基本的功能。这一功能被用来创建封装了解释器的特殊关键字。
一个宏可以由任意多个合法的标识符组成,如:modify
考虑下面的代码:
modify (obj) { value = 'foo' };
这不是一个合法的MVEL表达式,因为毕竟在MVEL中没有modify这个关键字。然而,在JBoss Drools 中会用这一功能通过用字符串
@Modify with替代modify标识符来实现结构变化监听器,
1、org.mvel.Macro 接口:
public interface Macro {
public String doMacro();
}
这个接口非常简单,执行时方法 doMacro() 会返回一个字符串来代替原有的标记,例如:
Macro modifyMacro = new Macro() {
public String doMacro() {
return "@Modify with";
}
}
2、使用MacroProcessor
MacroProcessor是一个轻量级的快速文本转换器,它使用定义的宏来替代所有相对应的标识符。定义的宏存储在一个Map中,需要匹配的标识符作为
键,宏的实例作为值,然后传递给MacroProcessor,如:
Map<String, Macro> myMacros = new HashMap<String, Macro>();
// Add modifyMacro to the Map
myMacros.put("modify", modifyMacro);
// Create the macro processor
MacroProcessor macroProcessor = new MacroProcessor();
// Add the macro map to the macro processor
macroProcessor.setMacros(myMacros);
// Now we pre-parse our expression
String parsedExpression = macroProcessor.parse(expression);
返回的字符串就会直接传递给MVEL的编译器。
十三、拦截器
MVEL提供了在编译后的表达式里使用拦截器的功能,这对实现监听器或是在表达式内部触发一个外部事件特别有用。声明拦截器用的是@Syntax,
有点像java语言中的注解。拦截器的声明应该放在待封装的语句之前,它可以实现之前或之后的监听器,或二者都实现。例如:
@Intercept
foreach (item : fooItems) {
total += fooItems.price;
}
在这个特殊的句子里,拦截器封装了整个的foreach块,因此,如果拦截器实现了之后的监听器,则当foreach循环结束后,拦截动作将被触发。
1、拦截器接口org.mvel.intergration.Interceptor
public interface Interceptor {
public int doBefore(ASTNode node, VariableResolverFactory factory);
public int doAfter(Object exitStackValue, ASTNode node, VariableResolverFactory factory);
}
拦截器接口提供了两个待实现的方法:doBefore和doAfter,下面我们来看一下MVEL运行时传递给这两个方法的参数的含义
2、doBefore
在执行封装的命令前会执行doBefore方法。
org.mvel.ASTNode::node
ASTNode句柄是 拦截器内部ASTNode 的一个引用,可以用来获取实际编译后的代码的信息。
org.mvel.integration.VariableResolverFactory::factory
变量分析器工厂提供表达式内当前范围内变量的访问权限。
3、doAfter
在执行完封装的指令后执行doAfter方法
java.lang.Object::exitStackValue
doAfter方法虽是在语句执行后执行,但却不是在帧结束前。因此,操作结束时留在栈中的任何数据都仍然存在,而且能被拦截器访问。例如:
@Intercept cost += value;
这是一个比较特殊的句子,cost的原值一直保存在栈中,直到整个帧执行完毕,因此,这个值在调用doAfter方法时可以通过exitStackValue访问到。
org.mvel.ASTNode::node
这是传递到doBefore方法中的同一个AST 元素,更多细节参考doBefore方法。
org.mvel.intergration.VariableResolverFactory::factory
同doBefore方法
4、编译器中使用拦截器
为了能是拦截器连到表达式中,必须在编译表达式之前提供拦截器,因此有一点需要注意,拦截器可能不用于MVEL解释器。
拦截器是储存在map里提供给编译器的,map中的键为拦截器的名称,值为拦截器实例。如:
// Create a new ParserContext
ParserContext context = new ParserContext();
Map<String, Interceptor> myInterceptors = new HashMap<String, Interceptor>();
// Create a simple interceptor.
Interceptor myInterceptor = new Interceptor() {
public int doBefore(ASTNode node, VariableResolverFactory factory) {
System.out.println("BEFORE!");
}
public int doAfter((Object value, ASTNode node, VariableResolverFactory factory) {
System.out.println("AFTER!");
}
};
// Now add the interceptor to the map.
myInterceptors.put("Foo", myInterceptor);
// Add the interceptors map to the parser context.
context.setInterceptors(myInterceptors);
// Compile the expression.
Serializable compiledExpression = MVEL.compileExpression(expression, context);
十四、数据类型
MVEL是一种有静态类型的动态类型语言。大部分MVEL使用者都比较倾向于用动态类型,因为它非常简单易用。如:
a = 10; // declare a variable 'a'
b = 15; // declare a variable 'b';
a + b;
1、动态类型与强制转换
像MVEL这种直接与java对象(静态类型)打交道的语言,最重要的一个方面就是强制类型转换。因为MVEL不能对一个java.lang.String对象和一个
java.lang.Integer对象进行数学运算,所以就必须把其中一个的类型转换成另一个的类型。
2、性能考虑
在你的应用中集成一个像MVEL这样的东西,性能考虑是必须的。对于重量级程序加载,强制类型转换超负荷等可以通过缓存和优化器(仅用于预编译
的表达式)来解决。然而,并不是所有的强制类型转换都可以忽略不管,关键要看它是在做什么。
比如,当一个String类型的变量在运行中要看成一个整形变量时,要阻止运行时将字符串转换成整型简直是不可能的,像这种情况,一定要考虑其性能。
3、方法调用
调用方法是强制转换的最重要的一个方面。从根本上讲,你可以直接调用,而无需关心参数是什么。解释器或编译器会分析方法的参数类型,然后确定
要进行哪一种强制转换,如果是重载的方法,它会选择与输入类型最接近的那个方法进行调用,以尽可能的避免强制转换。
4、数组
数组是强制类型转换中最有趣的一个方面,因为MVEL缺省使用无类型数组(也就是说任何情况下都是Object[]),只有当遇到类型冲突时,才会尝试将
整个数组转换成所需的类型,比如在方法调用传参时。
示例:
myArray = {1,2,3};
// pass to method that accepts String[]
myObject.someMethod(myArray);
在这个例子里,somMethod方法接收字符数组,这在MVEL中不会出错,相反,MVEL会将myArray转换成字符数组。
5、静态类型
静态类型与java类似,只不过默认情况下仍然会进行强制转换。
int num = 10;
这个句子声明了一个整型变量num,这时,MVEL运行时会强制转换类型。比如,声明后赋值一个不合适类型的数据,结果就会出现异常。
num = new HashMap(); // will throw an incompatible typing exception.
但如果是一个可以进行强制类型转换的值时,MVEL就会进行强制转换。
num = "100"; // will work -- parses String to an integer.
6、严格类型
严格类型是编译器的一种可选模式,在这种模式下,所有的类型都必须限定,不管是声明时还是在引用时。
启动严格模式:
当编译一个表达式时,可以通过ParserContext设置setStrictTypeEnforcement(true)将编译器设置成严格模式。
严格类型通过表达式内的具体类型声明或提前告诉转换器确定的类型来完成。例如:
ExpressionCompiler compiler = new ExpressionCompiler(expr);
ParserContext context = new ParserContext();
context.setStrictTypeEnforcement(true);
context.addInput("message", Message.class);
context.addInput("person", Person.class);
compiler.compile(context);
在这个例子中我们通知编译器表达式将接收两个外部输入:message 和 person 及它们的类型。这就使得编译器可以在编译时确定某一
个调用是否是安全的,从而防止了运行时的错误。
7、强类型
强类型是MVEL2.0新引入的概念。强类型模式要求所有的变量必须是限定的类型,从这一点上它与严格类型不同。差别在于严格模式只是在编译时
限定属性和方法调用的类型。
十五、Shell
通过交互式的Shell,你可以直接与MVEL打交道,去探究MVEL的特性。
1、运行Shell
只需运行MVEL的分布式jar包既可运行Shell:java -jar mvel2-2.0.jar
或者,你也可以在你喜欢的IDE中通过配置一个该类的运行环境来运行。
十六、FAQ
1、为什么不能使用.class的引用?
MVEL没有像java中的用来执行类型文件的.class标识符,其实它本身就没有class文件,而只需要通过其名称就可以引用这个类。比如,一个方法
接收一个Class类型作为参数,你可以这样来调用:
// MVEL
someMethod(String);
// Java-equivalent
someMethod(String.class);
事实上,MVEL将.class视作一个普通的bean属性,因此,如果使用String。class,那返回值就会是指向java.lang.Class本身的一个
java.lang.Class 的实例,因此就相当于在java中使用String.class.getClass() .
原理是这样的,MVEL使用动态类型系统,这样类型就被当作普通的变量来看待,而不是像java中限定类文件。所以,MVEL允许class类型作为
一个普通变量来引用,而不像java。
十七、为什么不能用object.class.name的格式?
这是MVEL的一个限制,可能会在将来的某个版本中标记出来,但bean属性不支持对Class的引用。并不是说不能调用Class的方法,你必须使用
限定的方法,像:
someVar.class.getName(); // Yes!
someVar.class.name; // No!
someVar.getClass().getName() // Yes!
someVar.getClass().name // No!
这一规定完全限制了java.lang.Class仅可用做某个变量的属性,并限制了MVEL处理类的引用的方式。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/234349.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...