ObjectUtils.java 28.7 KB
Newer Older
1
/*
J
Juergen Hoeller 已提交
2
 * Copyright 2002-2019 the original author or authors.
3 4 5 6 7
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
S
Spring Operator 已提交
8
 *      https://www.apache.org/licenses/LICENSE-2.0
9 10 11 12 13 14 15 16 17 18 19 20
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.util;

import java.lang.reflect.Array;
import java.util.Arrays;
21 22
import java.util.Collection;
import java.util.Map;
23
import java.util.Optional;
24
import java.util.StringJoiner;
25

26 27
import org.springframework.lang.Nullable;

28
/**
J
Juergen Hoeller 已提交
29
 * Miscellaneous object utility methods.
30 31
 *
 * <p>Mainly for internal use within the framework.
J
Juergen Hoeller 已提交
32 33
 *
 * <p>Thanks to Alex Ruiz for contributing several enhancements to this class!
34 35 36 37 38
 *
 * @author Juergen Hoeller
 * @author Keith Donald
 * @author Rod Johnson
 * @author Rob Harrop
39
 * @author Chris Beams
40
 * @author Sam Brannen
41
 * @since 19.03.2004
42 43 44
 * @see ClassUtils
 * @see CollectionUtils
 * @see StringUtils
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
 */
public abstract class ObjectUtils {

	private static final int INITIAL_HASH = 7;
	private static final int MULTIPLIER = 31;

	private static final String EMPTY_STRING = "";
	private static final String NULL_STRING = "null";
	private static final String ARRAY_START = "{";
	private static final String ARRAY_END = "}";
	private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;
	private static final String ARRAY_ELEMENT_SEPARATOR = ", ";


	/**
	 * Return whether the given throwable is a checked exception:
	 * that is, neither a RuntimeException nor an Error.
	 * @param ex the throwable to check
	 * @return whether the throwable is a checked exception
	 * @see java.lang.Exception
	 * @see java.lang.RuntimeException
	 * @see java.lang.Error
	 */
	public static boolean isCheckedException(Throwable ex) {
		return !(ex instanceof RuntimeException || ex instanceof Error);
	}

	/**
73 74 75 76
	 * Check whether the given exception is compatible with the specified
	 * exception types, as declared in a throws clause.
	 * @param ex the exception to check
	 * @param declaredExceptions the exception types declared in the throws clause
77 78
	 * @return whether the given exception is compatible
	 */
79
	public static boolean isCompatibleWithThrowsClause(Throwable ex, @Nullable Class<?>... declaredExceptions) {
80 81 82 83
		if (!isCheckedException(ex)) {
			return true;
		}
		if (declaredExceptions != null) {
84 85
			for (Class<?> declaredException : declaredExceptions) {
				if (declaredException.isInstance(ex)) {
86 87 88 89 90 91 92 93
					return true;
				}
			}
		}
		return false;
	}

	/**
94 95 96 97
	 * Determine whether the given object is an array:
	 * either an Object array or a primitive array.
	 * @param obj the object to check
	 */
98
	public static boolean isArray(@Nullable Object obj) {
99 100 101 102 103
		return (obj != null && obj.getClass().isArray());
	}

	/**
	 * Determine whether the given array is empty:
104
	 * i.e. {@code null} or of zero length.
105
	 * @param array the array to check
106
	 * @see #isEmpty(Object)
107
	 */
108
	public static boolean isEmpty(@Nullable Object[] array) {
109 110 111
		return (array == null || array.length == 0);
	}

112 113 114 115
	/**
	 * Determine whether the given object is empty.
	 * <p>This method supports the following object types.
	 * <ul>
116
	 * <li>{@code Optional}: considered empty if {@link Optional#empty()}</li>
117 118 119 120 121 122 123 124 125 126
	 * <li>{@code Array}: considered empty if its length is zero</li>
	 * <li>{@link CharSequence}: considered empty if its length is zero</li>
	 * <li>{@link Collection}: delegates to {@link Collection#isEmpty()}</li>
	 * <li>{@link Map}: delegates to {@link Map#isEmpty()}</li>
	 * </ul>
	 * <p>If the given object is non-null and not one of the aforementioned
	 * supported types, this method returns {@code false}.
	 * @param obj the object to check
	 * @return {@code true} if the object is {@code null} or <em>empty</em>
	 * @since 4.2
127
	 * @see Optional#isPresent()
128 129 130 131 132 133 134
	 * @see ObjectUtils#isEmpty(Object[])
	 * @see StringUtils#hasLength(CharSequence)
	 * @see StringUtils#isEmpty(Object)
	 * @see CollectionUtils#isEmpty(java.util.Collection)
	 * @see CollectionUtils#isEmpty(java.util.Map)
	 */
	@SuppressWarnings("rawtypes")
135
	public static boolean isEmpty(@Nullable Object obj) {
136 137 138 139
		if (obj == null) {
			return true;
		}

140 141 142
		if (obj instanceof Optional) {
			return !((Optional) obj).isPresent();
		}
143 144 145
		if (obj instanceof CharSequence) {
			return ((CharSequence) obj).length() == 0;
		}
146 147 148
		if (obj.getClass().isArray()) {
			return Array.getLength(obj) == 0;
		}
149 150 151 152 153 154 155 156 157 158 159
		if (obj instanceof Collection) {
			return ((Collection) obj).isEmpty();
		}
		if (obj instanceof Map) {
			return ((Map) obj).isEmpty();
		}

		// else
		return false;
	}

160 161 162 163 164 165 166
	/**
	 * Unwrap the given object which is potentially a {@link java.util.Optional}.
	 * @param obj the candidate object
	 * @return either the value held within the {@code Optional}, {@code null}
	 * if the {@code Optional} is empty, or simply the given object as-is
	 * @since 5.0
	 */
167
	@Nullable
168
	public static Object unwrapOptional(@Nullable Object obj) {
169 170 171 172 173 174 175 176 177 178 179 180
		if (obj instanceof Optional) {
			Optional<?> optional = (Optional<?>) obj;
			if (!optional.isPresent()) {
				return null;
			}
			Object result = optional.get();
			Assert.isTrue(!(result instanceof Optional), "Multi-level Optional usage not supported");
			return result;
		}
		return obj;
	}

181 182
	/**
	 * Check whether the given array contains the given element.
183 184
	 * @param array the array to check (may be {@code null},
	 * in which case the return value will always be {@code false})
185 186 187
	 * @param element the element to check for
	 * @return whether the element has been found in the given array
	 */
188
	public static boolean containsElement(@Nullable Object[] array, Object element) {
189 190 191
		if (array == null) {
			return false;
		}
192 193
		for (Object arrayEle : array) {
			if (nullSafeEquals(arrayEle, element)) {
194 195 196 197 198 199
				return true;
			}
		}
		return false;
	}

200 201 202
	/**
	 * Check whether the given array of enum constants contains a constant with the given name,
	 * ignoring case when determining a match.
J
Juergen Hoeller 已提交
203
	 * @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()}
204 205 206 207 208 209 210 211 212
	 * @param constant the constant name to find (must not be null or empty string)
	 * @return whether the constant has been found in the given array
	 */
	public static boolean containsConstant(Enum<?>[] enumValues, String constant) {
		return containsConstant(enumValues, constant, false);
	}

	/**
	 * Check whether the given array of enum constants contains a constant with the given name.
J
Juergen Hoeller 已提交
213
	 * @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()}
214 215 216 217 218 219
	 * @param constant the constant name to find (must not be null or empty string)
	 * @param caseSensitive whether case is significant in determining a match
	 * @return whether the constant has been found in the given array
	 */
	public static boolean containsConstant(Enum<?>[] enumValues, String constant, boolean caseSensitive) {
		for (Enum<?> candidate : enumValues) {
J
Juergen Hoeller 已提交
220
			if (caseSensitive ? candidate.toString().equals(constant) :
221 222 223 224 225 226 227 228 229 230
					candidate.toString().equalsIgnoreCase(constant)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Case insensitive alternative to {@link Enum#valueOf(Class, String)}.
	 * @param <E> the concrete Enum type
J
Juergen Hoeller 已提交
231
	 * @param enumValues the array of all Enum constants in question, usually per {@code Enum.values()}
232 233 234 235 236 237
	 * @param constant the constant to get the enum value of
	 * @throws IllegalArgumentException if the given constant is not found in the given array
	 * of enum values. Use {@link #containsConstant(Enum[], String)} as a guard to avoid this exception.
	 */
	public static <E extends Enum<?>> E caseInsensitiveValueOf(E[] enumValues, String constant) {
		for (E candidate : enumValues) {
J
Juergen Hoeller 已提交
238
			if (candidate.toString().equalsIgnoreCase(constant)) {
239 240 241
				return candidate;
			}
		}
J
Juergen Hoeller 已提交
242 243
		throw new IllegalArgumentException("Constant [" + constant + "] does not exist in enum type " +
				enumValues.getClass().getComponentType().getName());
244 245
	}

246
	/**
247 248
	 * Append the given object to the given array, returning a new array
	 * consisting of the input array contents plus the given object.
249
	 * @param array the array to append to (can be {@code null})
250
	 * @param obj the object to append
251
	 * @return the new array (of the same component type; never {@code null})
252
	 */
W
www 已提交
253
	public static <A, O extends A> A[] addObjectToArray(@Nullable A[] array, @Nullable O obj) {
254
		Class<?> compType = Object.class;
255 256 257 258 259 260 261
		if (array != null) {
			compType = array.getClass().getComponentType();
		}
		else if (obj != null) {
			compType = obj.getClass();
		}
		int newArrLength = (array != null ? array.length + 1 : 1);
262 263
		@SuppressWarnings("unchecked")
		A[] newArr = (A[]) Array.newInstance(compType, newArrLength);
264 265 266 267 268 269 270 271 272 273
		if (array != null) {
			System.arraycopy(array, 0, newArr, 0, array.length);
		}
		newArr[newArr.length - 1] = obj;
		return newArr;
	}

	/**
	 * Convert the given array (which may be a primitive array) to an
	 * object array (if necessary of primitive wrapper objects).
274
	 * <p>A {@code null} source value will be converted to an
275 276
	 * empty Object array.
	 * @param source the (potentially primitive) array
277
	 * @return the corresponding object array (never {@code null})
278 279
	 * @throws IllegalArgumentException if the parameter is not an array
	 */
280
	public static Object[] toObjectArray(@Nullable Object source) {
281 282 283 284 285 286 287 288 289 290 291 292 293
		if (source instanceof Object[]) {
			return (Object[]) source;
		}
		if (source == null) {
			return new Object[0];
		}
		if (!source.getClass().isArray()) {
			throw new IllegalArgumentException("Source is not an array: " + source);
		}
		int length = Array.getLength(source);
		if (length == 0) {
			return new Object[0];
		}
P
Phillip Webb 已提交
294
		Class<?> wrapperType = Array.get(source, 0).getClass();
295 296 297 298 299 300 301 302 303 304 305 306 307
		Object[] newArray = (Object[]) Array.newInstance(wrapperType, length);
		for (int i = 0; i < length; i++) {
			newArray[i] = Array.get(source, i);
		}
		return newArray;
	}


	//---------------------------------------------------------------------
	// Convenience methods for content-based equality/hash-code handling
	//---------------------------------------------------------------------

	/**
308 309
	 * Determine if the given objects are equal, returning {@code true} if
	 * both are {@code null} or {@code false} if only one is {@code null}.
310
	 * <p>Compares arrays with {@code Arrays.equals}, performing an equality
311 312 313 314
	 * check based on the array elements rather than the array reference.
	 * @param o1 first Object to compare
	 * @param o2 second Object to compare
	 * @return whether the given objects are equal
315
	 * @see Object#equals(Object)
316 317
	 * @see java.util.Arrays#equals
	 */
318
	public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
319 320 321 322 323 324 325 326 327 328
		if (o1 == o2) {
			return true;
		}
		if (o1 == null || o2 == null) {
			return false;
		}
		if (o1.equals(o2)) {
			return true;
		}
		if (o1.getClass().isArray() && o2.getClass().isArray()) {
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
			return arrayEquals(o1, o2);
		}
		return false;
	}

	/**
	 * Compare the given arrays with {@code Arrays.equals}, performing an equality
	 * check based on the array elements rather than the array reference.
	 * @param o1 first array to compare
	 * @param o2 second array to compare
	 * @return whether the given objects are equal
	 * @see #nullSafeEquals(Object, Object)
	 * @see java.util.Arrays#equals
	 */
	private static boolean arrayEquals(Object o1, Object o2) {
		if (o1 instanceof Object[] && o2 instanceof Object[]) {
			return Arrays.equals((Object[]) o1, (Object[]) o2);
		}
		if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
			return Arrays.equals((boolean[]) o1, (boolean[]) o2);
		}
		if (o1 instanceof byte[] && o2 instanceof byte[]) {
			return Arrays.equals((byte[]) o1, (byte[]) o2);
		}
		if (o1 instanceof char[] && o2 instanceof char[]) {
			return Arrays.equals((char[]) o1, (char[]) o2);
		}
		if (o1 instanceof double[] && o2 instanceof double[]) {
			return Arrays.equals((double[]) o1, (double[]) o2);
		}
		if (o1 instanceof float[] && o2 instanceof float[]) {
			return Arrays.equals((float[]) o1, (float[]) o2);
		}
		if (o1 instanceof int[] && o2 instanceof int[]) {
			return Arrays.equals((int[]) o1, (int[]) o2);
		}
		if (o1 instanceof long[] && o2 instanceof long[]) {
			return Arrays.equals((long[]) o1, (long[]) o2);
		}
		if (o1 instanceof short[] && o2 instanceof short[]) {
			return Arrays.equals((short[]) o1, (short[]) o2);
370 371 372 373 374 375
		}
		return false;
	}

	/**
	 * Return as hash code for the given object; typically the value of
P
Phillip Webb 已提交
376
	 * {@code Object#hashCode()}}. If the object is an array,
377 378
	 * this method will delegate to any of the {@code nullSafeHashCode}
	 * methods for arrays in this class. If the object is {@code null},
379
	 * this method returns 0.
380
	 * @see Object#hashCode()
381 382 383 384 385 386 387 388 389 390
	 * @see #nullSafeHashCode(Object[])
	 * @see #nullSafeHashCode(boolean[])
	 * @see #nullSafeHashCode(byte[])
	 * @see #nullSafeHashCode(char[])
	 * @see #nullSafeHashCode(double[])
	 * @see #nullSafeHashCode(float[])
	 * @see #nullSafeHashCode(int[])
	 * @see #nullSafeHashCode(long[])
	 * @see #nullSafeHashCode(short[])
	 */
391
	public static int nullSafeHashCode(@Nullable Object obj) {
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
		if (obj == null) {
			return 0;
		}
		if (obj.getClass().isArray()) {
			if (obj instanceof Object[]) {
				return nullSafeHashCode((Object[]) obj);
			}
			if (obj instanceof boolean[]) {
				return nullSafeHashCode((boolean[]) obj);
			}
			if (obj instanceof byte[]) {
				return nullSafeHashCode((byte[]) obj);
			}
			if (obj instanceof char[]) {
				return nullSafeHashCode((char[]) obj);
			}
			if (obj instanceof double[]) {
				return nullSafeHashCode((double[]) obj);
			}
			if (obj instanceof float[]) {
				return nullSafeHashCode((float[]) obj);
			}
			if (obj instanceof int[]) {
				return nullSafeHashCode((int[]) obj);
			}
			if (obj instanceof long[]) {
				return nullSafeHashCode((long[]) obj);
			}
			if (obj instanceof short[]) {
				return nullSafeHashCode((short[]) obj);
			}
		}
		return obj.hashCode();
	}

	/**
	 * Return a hash code based on the contents of the specified array.
429
	 * If {@code array} is {@code null}, this method returns 0.
430
	 */
431
	public static int nullSafeHashCode(@Nullable Object[] array) {
432 433 434 435
		if (array == null) {
			return 0;
		}
		int hash = INITIAL_HASH;
436 437
		for (Object element : array) {
			hash = MULTIPLIER * hash + nullSafeHashCode(element);
438 439 440 441 442 443
		}
		return hash;
	}

	/**
	 * Return a hash code based on the contents of the specified array.
444
	 * If {@code array} is {@code null}, this method returns 0.
445
	 */
446
	public static int nullSafeHashCode(@Nullable boolean[] array) {
447 448 449 450
		if (array == null) {
			return 0;
		}
		int hash = INITIAL_HASH;
451
		for (boolean element : array) {
452
			hash = MULTIPLIER * hash + Boolean.hashCode(element);
453 454 455 456 457 458
		}
		return hash;
	}

	/**
	 * Return a hash code based on the contents of the specified array.
459
	 * If {@code array} is {@code null}, this method returns 0.
460
	 */
461
	public static int nullSafeHashCode(@Nullable byte[] array) {
462 463 464 465
		if (array == null) {
			return 0;
		}
		int hash = INITIAL_HASH;
466 467
		for (byte element : array) {
			hash = MULTIPLIER * hash + element;
468 469 470 471 472 473
		}
		return hash;
	}

	/**
	 * Return a hash code based on the contents of the specified array.
474
	 * If {@code array} is {@code null}, this method returns 0.
475
	 */
476
	public static int nullSafeHashCode(@Nullable char[] array) {
477 478 479 480
		if (array == null) {
			return 0;
		}
		int hash = INITIAL_HASH;
481 482
		for (char element : array) {
			hash = MULTIPLIER * hash + element;
483 484 485 486 487 488
		}
		return hash;
	}

	/**
	 * Return a hash code based on the contents of the specified array.
489
	 * If {@code array} is {@code null}, this method returns 0.
490
	 */
491
	public static int nullSafeHashCode(@Nullable double[] array) {
492 493 494 495
		if (array == null) {
			return 0;
		}
		int hash = INITIAL_HASH;
496
		for (double element : array) {
497
			hash = MULTIPLIER * hash + Double.hashCode(element);
498 499 500 501 502 503
		}
		return hash;
	}

	/**
	 * Return a hash code based on the contents of the specified array.
504
	 * If {@code array} is {@code null}, this method returns 0.
505
	 */
506
	public static int nullSafeHashCode(@Nullable float[] array) {
507 508 509 510
		if (array == null) {
			return 0;
		}
		int hash = INITIAL_HASH;
511
		for (float element : array) {
512
			hash = MULTIPLIER * hash + Float.hashCode(element);
513 514 515 516 517 518
		}
		return hash;
	}

	/**
	 * Return a hash code based on the contents of the specified array.
519
	 * If {@code array} is {@code null}, this method returns 0.
520
	 */
521
	public static int nullSafeHashCode(@Nullable int[] array) {
522 523 524 525
		if (array == null) {
			return 0;
		}
		int hash = INITIAL_HASH;
526 527
		for (int element : array) {
			hash = MULTIPLIER * hash + element;
528 529 530 531 532 533
		}
		return hash;
	}

	/**
	 * Return a hash code based on the contents of the specified array.
534
	 * If {@code array} is {@code null}, this method returns 0.
535
	 */
536
	public static int nullSafeHashCode(@Nullable long[] array) {
537 538 539 540
		if (array == null) {
			return 0;
		}
		int hash = INITIAL_HASH;
541
		for (long element : array) {
542
			hash = MULTIPLIER * hash + Long.hashCode(element);
543 544 545 546 547 548
		}
		return hash;
	}

	/**
	 * Return a hash code based on the contents of the specified array.
549
	 * If {@code array} is {@code null}, this method returns 0.
550
	 */
551
	public static int nullSafeHashCode(@Nullable short[] array) {
552 553 554 555
		if (array == null) {
			return 0;
		}
		int hash = INITIAL_HASH;
556 557
		for (short element : array) {
			hash = MULTIPLIER * hash + element;
558 559 560 561 562
		}
		return hash;
	}

	/**
563 564
	 * Return the same value as {@link Boolean#hashCode(boolean)}}.
	 * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
565
	 */
566
	@Deprecated
567
	public static int hashCode(boolean bool) {
568
		return Boolean.hashCode(bool);
569 570 571
	}

	/**
572 573
	 * Return the same value as {@link Double#hashCode(double)}}.
	 * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
574
	 */
575
	@Deprecated
576
	public static int hashCode(double dbl) {
577
		return Double.hashCode(dbl);
578 579 580
	}

	/**
581 582
	 * Return the same value as {@link Float#hashCode(float)}}.
	 * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
583
	 */
584
	@Deprecated
585
	public static int hashCode(float flt) {
586
		return Float.hashCode(flt);
587 588 589
	}

	/**
590 591
	 * Return the same value as {@link Long#hashCode(long)}}.
	 * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
592
	 */
593
	@Deprecated
594
	public static int hashCode(long lng) {
595
		return Long.hashCode(lng);
596 597 598 599 600 601 602 603 604
	}


	//---------------------------------------------------------------------
	// Convenience methods for toString output
	//---------------------------------------------------------------------

	/**
	 * Return a String representation of an object's overall identity.
605
	 * @param obj the object (may be {@code null})
606
	 * @return the object's identity as String representation,
607
	 * or an empty String if the object was {@code null}
608
	 */
609
	public static String identityToString(@Nullable Object obj) {
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
		if (obj == null) {
			return EMPTY_STRING;
		}
		return obj.getClass().getName() + "@" + getIdentityHexString(obj);
	}

	/**
	 * Return a hex String form of an object's identity hash code.
	 * @param obj the object
	 * @return the object's identity code in hex notation
	 */
	public static String getIdentityHexString(Object obj) {
		return Integer.toHexString(System.identityHashCode(obj));
	}

	/**
626 627
	 * Return a content-based String representation if {@code obj} is
	 * not {@code null}; otherwise returns an empty String.
628
	 * <p>Differs from {@link #nullSafeToString(Object)} in that it returns
629
	 * an empty String rather than "null" for a {@code null} value.
630
	 * @param obj the object to build a display String for
631
	 * @return a display String representation of {@code obj}
632 633
	 * @see #nullSafeToString(Object)
	 */
634
	public static String getDisplayString(@Nullable Object obj) {
635 636 637 638 639 640 641 642
		if (obj == null) {
			return EMPTY_STRING;
		}
		return nullSafeToString(obj);
	}

	/**
	 * Determine the class name for the given object.
J
Juergen Hoeller 已提交
643
	 * <p>Returns a {@code "null"} String if {@code obj} is {@code null}.
644
	 * @param obj the object to introspect (may be {@code null})
645 646
	 * @return the corresponding class name
	 */
647
	public static String nullSafeClassName(@Nullable Object obj) {
648 649 650 651 652 653
		return (obj != null ? obj.getClass().getName() : NULL_STRING);
	}

	/**
	 * Return a String representation of the specified Object.
	 * <p>Builds a String representation of the contents in case of an array.
J
Juergen Hoeller 已提交
654
	 * Returns a {@code "null"} String if {@code obj} is {@code null}.
655
	 * @param obj the object to build a String representation for
656
	 * @return a String representation of {@code obj}
657
	 */
658
	public static String nullSafeToString(@Nullable Object obj) {
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
		if (obj == null) {
			return NULL_STRING;
		}
		if (obj instanceof String) {
			return (String) obj;
		}
		if (obj instanceof Object[]) {
			return nullSafeToString((Object[]) obj);
		}
		if (obj instanceof boolean[]) {
			return nullSafeToString((boolean[]) obj);
		}
		if (obj instanceof byte[]) {
			return nullSafeToString((byte[]) obj);
		}
		if (obj instanceof char[]) {
			return nullSafeToString((char[]) obj);
		}
		if (obj instanceof double[]) {
			return nullSafeToString((double[]) obj);
		}
		if (obj instanceof float[]) {
			return nullSafeToString((float[]) obj);
		}
		if (obj instanceof int[]) {
			return nullSafeToString((int[]) obj);
		}
		if (obj instanceof long[]) {
			return nullSafeToString((long[]) obj);
		}
		if (obj instanceof short[]) {
			return nullSafeToString((short[]) obj);
		}
		String str = obj.toString();
		return (str != null ? str : EMPTY_STRING);
	}

	/**
	 * Return a String representation of the contents of the specified array.
	 * <p>The String representation consists of a list of the array's elements,
699
	 * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
J
Juergen Hoeller 已提交
700 701
	 * by the characters {@code ", "} (a comma followed by a space).
	 * Returns a {@code "null"} String if {@code array} is {@code null}.
702
	 * @param array the array to build a String representation for
703
	 * @return a String representation of {@code array}
704
	 */
705
	public static String nullSafeToString(@Nullable Object[] array) {
706 707 708 709 710 711 712
		if (array == null) {
			return NULL_STRING;
		}
		int length = array.length;
		if (length == 0) {
			return EMPTY_ARRAY;
		}
713 714 715
		StringJoiner sj = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
		for (Object o : array) {
			sj.add(String.valueOf(o));
716
		}
717
		return sj.toString();
718 719 720 721 722
	}

	/**
	 * Return a String representation of the contents of the specified array.
	 * <p>The String representation consists of a list of the array's elements,
723
	 * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
J
Juergen Hoeller 已提交
724 725
	 * by the characters {@code ", "} (a comma followed by a space).
	 * Returns a {@code "null"} String if {@code array} is {@code null}.
726
	 * @param array the array to build a String representation for
727
	 * @return a String representation of {@code array}
728
	 */
729
	public static String nullSafeToString(@Nullable boolean[] array) {
730 731 732 733 734 735 736
		if (array == null) {
			return NULL_STRING;
		}
		int length = array.length;
		if (length == 0) {
			return EMPTY_ARRAY;
		}
737 738 739
		StringJoiner sj = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
		for (boolean b : array) {
			sj.add(String.valueOf(b));
740
		}
741
		return sj.toString();
742 743 744 745 746
	}

	/**
	 * Return a String representation of the contents of the specified array.
	 * <p>The String representation consists of a list of the array's elements,
747
	 * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
J
Juergen Hoeller 已提交
748 749
	 * by the characters {@code ", "} (a comma followed by a space).
	 * Returns a {@code "null"} String if {@code array} is {@code null}.
750
	 * @param array the array to build a String representation for
751
	 * @return a String representation of {@code array}
752
	 */
753
	public static String nullSafeToString(@Nullable byte[] array) {
754 755 756 757 758 759 760
		if (array == null) {
			return NULL_STRING;
		}
		int length = array.length;
		if (length == 0) {
			return EMPTY_ARRAY;
		}
761 762 763
		StringJoiner sj = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
		for (byte b : array) {
			sj.add(String.valueOf(b));
764
		}
765
		return sj.toString();
766 767 768 769 770
	}

	/**
	 * Return a String representation of the contents of the specified array.
	 * <p>The String representation consists of a list of the array's elements,
771
	 * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
J
Juergen Hoeller 已提交
772 773
	 * by the characters {@code ", "} (a comma followed by a space).
	 * Returns a {@code "null"} String if {@code array} is {@code null}.
774
	 * @param array the array to build a String representation for
775
	 * @return a String representation of {@code array}
776
	 */
777
	public static String nullSafeToString(@Nullable char[] array) {
778 779 780 781 782 783 784
		if (array == null) {
			return NULL_STRING;
		}
		int length = array.length;
		if (length == 0) {
			return EMPTY_ARRAY;
		}
785 786 787
		StringJoiner sj = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
		for (char c : array) {
			sj.add('\'' + String.valueOf(c) + '\'');
788
		}
789
		return sj.toString();
790 791 792 793 794
	}

	/**
	 * Return a String representation of the contents of the specified array.
	 * <p>The String representation consists of a list of the array's elements,
795
	 * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
J
Juergen Hoeller 已提交
796 797
	 * by the characters {@code ", "} (a comma followed by a space).
	 * Returns a {@code "null"} String if {@code array} is {@code null}.
798
	 * @param array the array to build a String representation for
799
	 * @return a String representation of {@code array}
800
	 */
801
	public static String nullSafeToString(@Nullable double[] array) {
802 803 804 805 806 807 808
		if (array == null) {
			return NULL_STRING;
		}
		int length = array.length;
		if (length == 0) {
			return EMPTY_ARRAY;
		}
809 810 811
		StringJoiner sj = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
		for (double d : array) {
			sj.add(String.valueOf(d));
812
		}
813
		return sj.toString();
814 815 816 817 818
	}

	/**
	 * Return a String representation of the contents of the specified array.
	 * <p>The String representation consists of a list of the array's elements,
819
	 * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
J
Juergen Hoeller 已提交
820 821
	 * by the characters {@code ", "} (a comma followed by a space).
	 * Returns a {@code "null"} String if {@code array} is {@code null}.
822
	 * @param array the array to build a String representation for
823
	 * @return a String representation of {@code array}
824
	 */
825
	public static String nullSafeToString(@Nullable float[] array) {
826 827 828 829 830 831 832
		if (array == null) {
			return NULL_STRING;
		}
		int length = array.length;
		if (length == 0) {
			return EMPTY_ARRAY;
		}
833 834 835
		StringJoiner sj = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
		for (float f : array) {
			sj.add(String.valueOf(f));
836
		}
837
		return sj.toString();
838 839 840 841 842
	}

	/**
	 * Return a String representation of the contents of the specified array.
	 * <p>The String representation consists of a list of the array's elements,
843
	 * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
J
Juergen Hoeller 已提交
844 845
	 * by the characters {@code ", "} (a comma followed by a space).
	 * Returns a {@code "null"} String if {@code array} is {@code null}.
846
	 * @param array the array to build a String representation for
847
	 * @return a String representation of {@code array}
848
	 */
849
	public static String nullSafeToString(@Nullable int[] array) {
850 851 852 853 854 855 856
		if (array == null) {
			return NULL_STRING;
		}
		int length = array.length;
		if (length == 0) {
			return EMPTY_ARRAY;
		}
857 858 859
		StringJoiner sj = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
		for (int i : array) {
			sj.add(String.valueOf(i));
860
		}
861
		return sj.toString();
862 863 864 865 866
	}

	/**
	 * Return a String representation of the contents of the specified array.
	 * <p>The String representation consists of a list of the array's elements,
867
	 * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
J
Juergen Hoeller 已提交
868 869
	 * by the characters {@code ", "} (a comma followed by a space).
	 * Returns a {@code "null"} String if {@code array} is {@code null}.
870
	 * @param array the array to build a String representation for
871
	 * @return a String representation of {@code array}
872
	 */
873
	public static String nullSafeToString(@Nullable long[] array) {
874 875 876 877 878 879 880
		if (array == null) {
			return NULL_STRING;
		}
		int length = array.length;
		if (length == 0) {
			return EMPTY_ARRAY;
		}
881 882 883
		StringJoiner sj = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
		for (long l : array) {
			sj.add(String.valueOf(l));
884
		}
885
		return sj.toString();
886 887 888 889 890
	}

	/**
	 * Return a String representation of the contents of the specified array.
	 * <p>The String representation consists of a list of the array's elements,
891
	 * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
J
Juergen Hoeller 已提交
892 893
	 * by the characters {@code ", "} (a comma followed by a space).
	 * Returns a {@code "null"} String if {@code array} is {@code null}.
894
	 * @param array the array to build a String representation for
895
	 * @return a String representation of {@code array}
896
	 */
897
	public static String nullSafeToString(@Nullable short[] array) {
898 899 900 901 902 903 904
		if (array == null) {
			return NULL_STRING;
		}
		int length = array.length;
		if (length == 0) {
			return EMPTY_ARRAY;
		}
905 906 907
		StringJoiner sj = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
		for (short s : array) {
			sj.add(String.valueOf(s));
908
		}
909
		return sj.toString();
910 911 912
	}

}