演算子

比較演算子

関係演算子 (等しい、等しくない、未満、以下、以上、以上) は、標準の演算子表記を使用することでサポートされます。これらの演算子は、Number 型だけでなく、Comparable を実装する型でも機能します。次のリストは、関係演算子の例をいくつか示しています。

  • Java

  • Kotlin

// evaluates to true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);

// evaluates to false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);

// evaluates to true
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);

// uses CustomValue:::compareTo
boolean trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean.class);
// evaluates to true
val trueValue = parser.parseExpression("2 == 2").getValue(Boolean::class.java)

// evaluates to false
val falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean::class.java)

// evaluates to true
val trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean::class.java)

// uses CustomValue:::compareTo
val trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean::class.java);

null との大小比較は、単純なルールに従います。null はゼロとしてではなく、ゼロとして扱われます。結果として、他の値は常に null より大きく(X > null は常に true)、他の値がゼロより小さくなることはありません(X < null は常に false です)。

代わりに数値比較を使用する場合は、ゼロとの比較を優先して、数値ベースの null 比較を避けます(たとえば、X > 0 または X < 0)。

各記号演算子は、純粋にテキスト上の等価物として指定することもできます。これにより、使用されるシンボルが、式が埋め込まれているドキュメント型 (XML ドキュメントなど) に対して特別な意味を持つ場合の問題が回避されます。同等のテキストは次のとおりです。

  • lt (<)

  • gt (>)

  • le (<=)

  • ge (>=)

  • eq (==)

  • ne (!=)

テキスト演算子はすべて大文字と小文字を区別しません。

標準の関係演算子に加えて、SpEL は betweeninstanceof、正規表現ベースの matches 演算子をサポートします。次のリストは、3 つすべての例を示しています。

  • Java

  • Kotlin

boolean result;

// evaluates to true
result = parser.parseExpression(
		"1 between {1, 5}").getValue(Boolean.class);

// evaluates to false
result = parser.parseExpression(
		"1 between {10, 15}").getValue(Boolean.class);

// evaluates to true
result = parser.parseExpression(
		"'elephant' between {'aardvark', 'zebra'}").getValue(Boolean.class);

// evaluates to false
result = parser.parseExpression(
		"'elephant' between {'aardvark', 'cobra'}").getValue(Boolean.class);

// evaluates to true
result = parser.parseExpression(
		"123 instanceof T(Integer)").getValue(Boolean.class);

// evaluates to false
result = parser.parseExpression(
		"'xyz' instanceof T(Integer)").getValue(Boolean.class);

// evaluates to true
result = parser.parseExpression(
		"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

// evaluates to false
result = parser.parseExpression(
		"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
// evaluates to true
var result = parser.parseExpression(
		"1 between {1, 5}").getValue(Boolean::class.java)

// evaluates to false
result = parser.parseExpression(
		"1 between {10, 15}").getValue(Boolean::class.java)

// evaluates to true
result = parser.parseExpression(
		"'elephant' between {'aardvark', 'zebra'}").getValue(Boolean::class.java)

// evaluates to false
result = parser.parseExpression(
		"'elephant' between {'aardvark', 'cobra'}").getValue(Boolean::class.java)

// evaluates to true
result = parser.parseExpression(
		"123 instanceof T(Integer)").getValue(Boolean::class.java)

// evaluates to false
result = parser.parseExpression(
		"'xyz' instanceof T(Integer)").getValue(Boolean::class.java)

// evaluates to true
result = parser.parseExpression(
		"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)

// evaluates to false
result = parser.parseExpression(
		"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)

between 演算子の構文は <input> between {<range_begin>, <range_end>} であり、事実上 <input> >= <range_begin> && <input> <= <range_end>} のショートカットです。

 1 between {1, 5} は true と評価され、1 between {5, 1} は false と評価されます。

プリミティブ型はすぐにラッパー型にボックス化されるため、注意してください。例: 1 instanceof T(int) は false に評価され、1 instanceof T(Integer) は true に評価されます。

論理演算子

SpEL は、次の論理 (boolean) 演算子をサポートします。

  • and (&&)

  • or (||)

  • not (!)

テキスト演算子はすべて大文字と小文字を区別しません。

次の例は、論理演算子の使用方法を示しています。

  • Java

  • Kotlin

// -- AND --

// evaluates to false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- OR --

// evaluates to true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- NOT --

// evaluates to false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);

// -- AND and NOT --

String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
// -- AND --

// evaluates to false
val falseValue = parser.parseExpression("true and false").getValue(Boolean::class.java)

// evaluates to true
val expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')"
val trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)

// -- OR --

// evaluates to true
val trueValue = parser.parseExpression("true or false").getValue(Boolean::class.java)

// evaluates to true
val expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')"
val trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)

// -- NOT --

// evaluates to false
val falseValue = parser.parseExpression("!true").getValue(Boolean::class.java)

// -- AND and NOT --

val expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"
val falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)

文字列演算子

文字列に対して次の演算子を使用できます。

  • 連結 (+)

  • 引き算 (-)

    • 単一の文字を含む文字列で使用します

  • 繰り返す (*)

次の例は、使用中の String 演算子を示しています。

  • Java

  • Kotlin

// -- Concatenation --

// evaluates to "hello world"
String helloWorld = parser.parseExpression("'hello' + ' ' + 'world'")
		.getValue(String.class);

// -- Character Subtraction --

// evaluates to 'a'
char ch = parser.parseExpression("'d' - 3")
		.getValue(char.class);

// -- Repeat --

// evaluates to "abcabc"
String repeated = parser.parseExpression("'abc' * 2")
		.getValue(String.class);
// -- Concatenation --

// evaluates to "hello world"
val helloWorld = parser.parseExpression("'hello' + ' ' + 'world'")
		.getValue(String::class.java)

// -- Character Subtraction --

// evaluates to 'a'
val ch = parser.parseExpression("'d' - 3")
		.getValue(Character::class.java);

// -- Repeat --

// evaluates to "abcabc"
val repeated = parser.parseExpression("'abc' * 2")
		.getValue(String::class.java);

数学演算子

数値に対して次の演算子を使用でき、標準の演算子の優先順位が適用されます。

  • 追加 (+)

  • 引き算 (-)

  • インクリメントよりも小さくなければなりません (++)

  • デクリメント (--)

  • 乗算 (*)

  • 分割 (/)

  • 係数 (%)

  • 指数関数的なパワー (^)

除算演算子と剰余演算子は、純粋にテキスト上の等価物として指定することもできます。これにより、使用されるシンボルが、式が埋め込まれているドキュメント型 (XML ドキュメントなど) に対して特別な意味を持つ場合の問題が回避されます。同等のテキストは次のとおりです。

  • div (/)

  • mod (%)

テキスト演算子はすべて大文字と小文字を区別しません。

インクリメント演算子とデクリメント演算子は、書き込み可能な変数またはプロパティを含む接頭辞 (++A--A) または後置表記 (A++A--) で使用できます。

次の例は、使用されている数学演算子を示しています。

  • Java

  • Kotlin

Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

// -- Addition --

int two = parser.parseExpression("1 + 1").getValue(int.class);  // 2

// -- Subtraction --

int four = parser.parseExpression("1 - -3").getValue(int.class);  // 4

double d = parser.parseExpression("1000.00 - 1e4").getValue(double.class);  // -9000

// -- Increment --

// The counter property in Inventor has an initial value of 0.

// evaluates to 2; counter is now 1
two = parser.parseExpression("counter++ + 2").getValue(context, inventor, int.class);

// evaluates to 5; counter is now 2
int five = parser.parseExpression("3 + ++counter").getValue(context, inventor, int.class);

// -- Decrement --

// The counter property in Inventor has a value of 2.

// evaluates to 6; counter is now 1
int six = parser.parseExpression("counter-- + 4").getValue(context, inventor, int.class);

// evaluates to 5; counter is now 0
five = parser.parseExpression("5 + --counter").getValue(context, inventor, int.class);

// -- Multiplication --

six = parser.parseExpression("-2 * -3").getValue(int.class);  // 6

double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(double.class);  // 24.0

// -- Division --

int minusTwo = parser.parseExpression("6 / -3").getValue(int.class);  // -2

double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(double.class);  // 1.0

// -- Modulus --

int three = parser.parseExpression("7 % 4").getValue(int.class);  // 3

int oneInt = parser.parseExpression("8 / 5 % 2").getValue(int.class);  // 1

// -- Exponential power --

int maxInt = parser.parseExpression("(2^31) - 1").getValue(int.class);  // Integer.MAX_VALUE

int minInt = parser.parseExpression("-2^31").getValue(int.class);  // Integer.MIN_VALUE

// -- Operator precedence --

int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(int.class);  // -21
val inventor = Inventor()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()

// -- Addition --

var two = parser.parseExpression("1 + 1").getValue(Int::class.java)  // 2

// -- Subtraction --

val four = parser.parseExpression("1 - -3").getValue(Int::class.java)  // 4

val d = parser.parseExpression("1000.00 - 1e4").getValue(Double::class.java)  // -9000

// -- Increment --

// The counter property in Inventor has an initial value of 0.

// evaluates to 2; counter is now 1
two = parser.parseExpression("counter++ + 2").getValue(context, inventor, Int::class.java)

// evaluates to 5; counter is now 2
var five = parser.parseExpression("3 + ++counter").getValue(context, inventor, Int::class.java)

// -- Decrement --

// The counter property in Inventor has a value of 2.

// evaluates to 6; counter is now 1
var six = parser.parseExpression("counter-- + 4").getValue(context, inventor, Int::class.java)

// evaluates to 5; counter is now 0
five = parser.parseExpression("5 + --counter").getValue(context, inventor, Int::class.java)

// -- Multiplication --

six = parser.parseExpression("-2 * -3").getValue(Int::class.java)  // 6

val twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double::class.java)  // 24.0

// -- Division --

val minusTwo = parser.parseExpression("6 / -3").getValue(Int::class.java)  // -2

val one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double::class.java)  // 1.0

// -- Modulus --

val three = parser.parseExpression("7 % 4").getValue(Int::class.java)  // 3

val oneInt = parser.parseExpression("8 / 5 % 2").getValue(Int::class.java)  // 1

// -- Exponential power --

val maxInt = parser.parseExpression("(2^31) - 1").getValue(Int::class.java)  // Integer.MAX_VALUE

val minInt = parser.parseExpression("-2^31").getValue(Int::class.java)  // Integer.MIN_VALUE

// -- Operator precedence --

val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java)  // -21

割り当て演算子

プロパティを設定するには、代入演算子(=)を使用します。これは通常、setValue の呼び出し内で実行されますが、getValue の呼び出し内で実行することもできます。次のリストは、代入演算子を使用する両方の方法を示しています。

  • Java

  • Kotlin

Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic");

// alternatively
String aleks = parser.parseExpression(
		"name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);
val inventor = Inventor()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()

parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic")

// alternatively
val aleks = parser.parseExpression(
		"name = 'Aleksandar Seovic'").getValue(context, inventor, String::class.java)

オーバーロードされた演算子

デフォルトでは、SpEL の Operation enum (ADDSUBTRACTDIVIDEMULTIPLYMODULUSPOWER) で定義された数学演算は、数値などの単純な型をサポートします。OperatorOverloader の実装を提供することにより、式言語は他の型に対するこれらの操作をサポートできます。

例: ADD 演算子をオーバーロードして、+ 記号を使用して 2 つのリストを連結できるようにする場合は、次のようにカスタム OperatorOverloader を実装できます。

pubic class ListConcatenation implements OperatorOverloader {

	@Override
	public boolean overridesOperation(Operation operation, Object left, Object right) {
		return (operation == Operation.ADD &&
				left instanceof List && right instanceof List);
	}

	@Override
	public Object operate(Operation operation, Object left, Object right) {
		if (operation == Operation.ADD &&
				left instanceof List list1 && right instanceof List list2) {

			List result = new ArrayList(list1);
			result.addAll(list2);
			return result;
		}
		throw new UnsupportedOperationException(
			"No overload for operation %s and operands [%s] and [%s]"
				.formatted(operation, left, right));
	}
}

ListConcatenation を StandardEvaluationContext の OperatorOverloader として登録すると、次の例に示すように {1, 2, 3} + {4, 5} のような式を評価できます。

  • Java

  • Kotlin

StandardEvaluationContext context = new StandardEvaluationContext();
context.setOperatorOverloader(new ListConcatenation());

// evaluates to a new list: [1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List.class);
StandardEvaluationContext context = StandardEvaluationContext()
context.setOperatorOverloader(ListConcatenation())

// evaluates to a new list: [1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List::class.java)

OperatorOverloader は、オペレーターのデフォルトのセマンティクスを変更しません。例: 上記の例の 2 + 2 は依然として 4 と評価されます。

オーバーロードされた演算子を使用する式はコンパイルできません。詳細については、コンパイラーの制限を参照してください。