Skip to content

Commit

Permalink
Add emptyArray and newArray to KiwiReflection2 (#276)
Browse files Browse the repository at this point in the history
These methods allow you to easily create an array of a given type
without needing to cast. One creates an empty array while the other
creates an array with a given length, and which contains only null
values.

The javadocs and initial version of the tests were "written" by ChatGPT.
I modified them slightly, and added a few more tests. For example, a
ValueSource can be used instead of a MethodSource when there is only one
test parameter. I also had to slightly correct the text in the throws
explanations in the javadoc, plus I added additional text including the
"see" references and the implNote text.
  • Loading branch information
sleberknight authored Jun 15, 2023
1 parent 932aac4 commit 626c9c9
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 0 deletions.
34 changes: 34 additions & 0 deletions src/main/java/org/kiwiproject/beta/reflect/KiwiReflection2.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.kiwiproject.beta.reflect;

import static com.google.common.base.Preconditions.checkArgument;
import static org.kiwiproject.base.KiwiPreconditions.checkArgumentNotNull;

import com.google.common.annotations.Beta;
import lombok.experimental.UtilityClass;
import org.checkerframework.checker.nullness.qual.NonNull;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
Expand Down Expand Up @@ -190,4 +192,36 @@ public static TypeInfo typeInformationOf(@NonNull Type type) {
return TypeInfo.ofType(type);
}

/**
* Creates an empty array of the specified type.
*
* @param <T> the type parameter representing the component type of the array
* @param type the class object representing the component type of the array
* @return an empty array of the specified type
* @throws IllegalArgumentException if type is null or is {@link Void#TYPE}
* @see Array#newInstance(Class, int)
* @implNote This method exists because {@link Array#newInstance(Class, int)} returns Object and thus
* requires a cast. Using this method, code can be a little cleaner without a cast.
*/
public static <T> T[] emptyArray(Class<T> type) {
return newArray(type, 0);
}

/**
* Creates a new array of the specified type and length. All values in the array are null.
*
* @param <T> the type parameter representing the component type of the array
* @param type the class object representing the component type of the array
* @param length the length of the new array
* @return a new array of the specified type and length
* @throws IllegalArgumentException if type is null or is {@link Void#TYPE}, or length is negative
* @see Array#newInstance(Class, int)
* @implNote This method exists because {@link Array#newInstance(Class, int)} returns Object and thus
* requires a cast. Using this method, code can be a little cleaner without a cast. */
@SuppressWarnings("unchecked")
public static <T> T[] newArray(Class<T> type, int length) {
checkArgumentNotNull(type);
checkArgument(length >= 0, "value must be positive or zero");
return (T[]) Array.newInstance(type, length);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,23 @@
import static org.junit.jupiter.api.Assertions.assertAll;

import lombok.Value;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.kiwiproject.beta.annotation.AccessedViaReflection;
import org.kiwiproject.beta.reflect.KiwiReflection2.JavaAccessModifier;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

@DisplayName("KiwiReflection2")
class KiwiReflection2Test {
Expand Down Expand Up @@ -374,4 +379,85 @@ static class Raw {
List rawList;
Map rawMap;
}

@Test
void emptyArray_withNullType_ShouldThrowIllegalArgumentException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> KiwiReflection2.emptyArray(null));
}

// This exists only to have the concrete type declared (the parameterized method below tests multiple generic types)
@Test
void emptyArray_shouldReturnEmptyArray() {
Integer[] result = KiwiReflection2.emptyArray(Integer.class);

assertThat(result).isEmpty();
}

@ParameterizedTest
@ValueSource(classes = {Integer.class, String.class, Boolean.class})
<T> void emptyArray_shouldReturnEmptyArray(Class<T> type) {
T[] result = KiwiReflection2.emptyArray(type);

assertThat(result).isEmpty();
}

@Test
void newArray_withNullTypeAndValidLength_ShouldThrowIllegalArgumentException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> KiwiReflection2.newArray(null, 10));
}

// This exists only to have the concrete type declared (the parameterized method below tests multiple generic types)
@Test
void newArray_shouldReturnArrayWithSpecifiedLength() {
Long[] result = KiwiReflection2.newArray(Long.class, 5);

assertThat(result).hasSize(5).containsOnlyNulls();
}

@Test
void newArray_shouldReturnArrayWithSpecifiedLength_OfZero() {
String[] result = KiwiReflection2.newArray(String.class, 0);

assertThat(result).isEmpty();
}

@ParameterizedTest
@MethodSource("newArrayTypeAndLengthProvider")
<T> void newArray_shouldReturnArrayWithSpecifiedLength(Class<T> type, int length) {
T[] result = KiwiReflection2.newArray(type, length);

assertThat(result).hasSize(length).containsOnlyNulls();
}

// This exists only to have the concrete type declared (the parameterized method below tests multiple generic types)
@Test
void newArray_withNegativeLength_shouldThrowIllegalArgumentException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> KiwiReflection2.newArray(Double.class, -1));
}

@ParameterizedTest
@MethodSource("newArrayNegativeLengthProvider")
void newArray_withNegativeLength_shouldThrowIllegalArgumentException(Class<?> type, int length) {
assertThatIllegalArgumentException()
.isThrownBy(() -> KiwiReflection2.newArray(type, length));
}

static Stream<Arguments> newArrayTypeAndLengthProvider() {
return Stream.of(
Arguments.of(Integer.class, 5),
Arguments.of(String.class, 10),
Arguments.of(Boolean.class, 25)
);
}

static Stream<Arguments> newArrayNegativeLengthProvider() {
return Stream.of(
Arguments.of(Double.class, -1),
Arguments.of(Character.class, -5),
Arguments.of(String.class, -42)
);
}
}

0 comments on commit 626c9c9

Please sign in to comment.